日期:2014-05-17  浏览次数:20886 次

为什么AutoResetEvent变量不能传递给线程?
实现多线程同时向多个目标地点发送一个文件。

程序如下
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的时候它的值已经变成release