ManualResetEvent 抛出 NullReferenceException:对象引用未设置为对象的实例

ManualResetEvent throws NullReferenceException: Object reference not set to an instance of an object

代码抛出

NullReferenceException: Object reference not set to an instance of an object

在线((ManualResetEvent)handles[i]).Set()。我在调试时检查了 handles[i] 是否有值。我做错了什么?

`               string[] fileEntries = Directory.GetFiles(pathFife);
                ManualResetEvent[] handles = new ManualResetEvent[fileEntries.Count()];
                int i = 0;
                foreach (string fullName in fileEntries)
                {
                    handles[i] = new ManualResetEvent(false);
                        var thread = new Thread(() =>
                            {
                                AddFile(fullName, month, year, user);
                                ((ManualResetEvent)handles[i]).Set();
                            });
                        thread.Start();
                    i++;
                }
                WaitHandle.WaitAll(handles);`     

发生的事情是你在 i 上使用了 a modified closure,它在线程内部使用。

i 的值在 用于 ((ManualResetEvent)handles[i]).Set(); 之前递增,此时您还没有设置 handles[i].

发生这种情况是因为调用线程在新线程执行 ((ManualResetEvent)handles[i]).Set(); 之前立即继续执行下一行代码 i++;。这是一个经典的竞争条件。

要解决此问题,请在启动线程之前添加以下行:

int j = i;

然后在((ManualResetEvent)handles[i]).Set();中使用j代替i

foreach (string fullName in fileEntries)
{
    handles[i] = new ManualResetEvent(false);
    int j = i;
    var thread = new Thread(() =>
    {
        AddFile(fullName, month, year, user);
        ((ManualResetEvent)handles[j]).Set();
    });
    thread.Start();
    i++;
}

当然,当您 运行 调试器下的代码时,线程已完全改变,因此您没有发现问题。