为啥AutoResetEvent变量不能传递给线程
为什么AutoResetEvent变量不能传递给线程?
实现多线程同时向多个目标地点发送一个文件。
程序如下
SendFile内部:
一运行就报错:
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at UpdateKav.Program.<>c__DisplayClass4.<Main>b__0()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
我把
改成
并且把WaitHandle.WaitAll(are);这行注释掉,程序就OK。
求高手解释!
------解决方案--------------------
索引越界,明显和AutoResetEvent无关啊。
---------------------------------------------
注意C#中闭包捕获的是“变量”,而不是“变量的当前值”。
你的代码中闭包捕获了are和_h两个变量,are是不会变化的,但是_h是会变的
_h最终的值是releasePaths.Count,超出了are的UpperBound,而后面创建的线程访问are[_h]时主线程很可能已经将_h的值更改为releasePath.Count了,所以高几率越界。
如果你是先创建所有线程,然后一起start(),所有线程在访问_h的时候它的值已经变成releasePaths.Count了,访问are[_h]百分百越界。
当然这里的“捕获变量”是从语意的角度来说的,编译器的实现并不是让lambda访问原始变量的地址。
实现多线程同时向多个目标地点发送一个文件。
程序如下
- C# code
AutoResetEvent[] are = new AutoResetEvent[releasePaths.Count]; int _h = 0; foreach (var item in releasePaths) { Console.WriteLine("In:_h={0}", _h); are[_h] = new AutoResetEvent(false); Thread thread = new Thread(() => item.SendFile("2012-9-11.zip", are[_h])); _h++; thread.Start(); } WaitHandle.WaitAll(are);
SendFile内部:
- C# code
public bool SendFile(string from, AutoResetEvent autoRE = null) { try { if (lowPath.StartsWith("ftp://")) { try { FTPclient ftpClient = new FTPclient(lowPath, User, Password); return ftpClient.Upload(from, lowPath); } catch (Exception) { return false; } } else { if (!DstPath.EndsWith("\\")) { DstPath += "\\"; } int times = 0; while (times < 10) { try { File.Copy(from, DstPath + System.IO.Path.GetFileName(from), true); return true; } catch (Exception e) { lock (ReleasePath._LockObj) { Log.WriteLog("Exception: " + e.Message); } Thread.Sleep(300000); } times++; } return false; } } finally { if (autoRE != null) { autoRE.Set(); } } }
一运行就报错:
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at UpdateKav.Program.<>c__DisplayClass4.<Main>b__0()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
我把
- C# code
Thread thread = new Thread(() => item.SendFile("2012-9-11.zip", are[_h]));
改成
- C# code
Thread thread = new Thread(() => item.SendFile("2012-9-11.zip", null));
并且把WaitHandle.WaitAll(are);这行注释掉,程序就OK。
求高手解释!
------解决方案--------------------
索引越界,明显和AutoResetEvent无关啊。
---------------------------------------------
注意C#中闭包捕获的是“变量”,而不是“变量的当前值”。
你的代码中闭包捕获了are和_h两个变量,are是不会变化的,但是_h是会变的
_h最终的值是releasePaths.Count,超出了are的UpperBound,而后面创建的线程访问are[_h]时主线程很可能已经将_h的值更改为releasePath.Count了,所以高几率越界。
如果你是先创建所有线程,然后一起start(),所有线程在访问_h的时候它的值已经变成releasePaths.Count了,访问are[_h]百分百越界。
当然这里的“捕获变量”是从语意的角度来说的,编译器的实现并不是让lambda访问原始变量的地址。