Parallel.ForEach IndexOutOfRangeException

Parallel.ForEach IndexOutOfRangeException

我正在尝试使用 Parallel.ForEach 浏览我的事件日志。但它随机停止并在 foreach 上出现错误 IndexOutOfRangeException。

这个:

foreach (EventLogEntry message in evlog.Entries)

有时在第一次循环时停止,有时在循环 44 次后停止。

此外,有时我会收到给定 ID 已存在的错误。

这是我的完整代码:

string ID = null;
Logs.Add("System", 0);
Logs.Add("Application", 1000);
Logs.Add("Setup", 2000);
Logs.Add("Forwarded Events", 3000);  

EventLog evlog = new EventLog();
evlog.MachineName = ".";  

Parallel.ForEach(Logs.Keys, logname =>
{
    System.Threading.Thread.Sleep(1000);
    evlog.Source = logname;
    string lognameWithoutSpaces = logname.Replace(" ", "");

    foreach (EventLogEntry message in evlog.Entries)
    {
        string type = message.EntryType.ToString();
        if (type == "0" | type == "Warning")
        {
            ID = lognameWithoutSpaces + "_" + Logs[logname].ToString();
            Console.WriteLine("ID: " + ID);
            dictLogs.Add(ID, new List<string>());
            dictLogs[ID].Add(evlog.Log);
            dictLogs[ID].Add(message.Source);
            dictLogs[ID].Add(message.InstanceId.ToString());
            dictLogs[ID].Add(type);
            dictLogs[ID].Add(message.UserName);
            dictLogs[ID].Add(message.TimeGenerated.ToString());
            dictLogs[ID].Add(message.Category);
            dictLogs[ID].Add(message.MachineName);
            dictLogs[ID].Add(message.Message);
            Logs[logname]++;
        }
    }
});
dictLogs.Clear();

这是我在控制台中看到的:

ID: ForwardedEvents_3000
ID: System_0
ID: Setup_2000
ID: Application_1000
'DashboardBackEnd.vshost.exe' (CLR v4.0.30319: DashboardBackEnd.vshost.exe):    Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.resources\v4.0_4.0.0.0_nl_b77a5c561934e089\System.resources.dll'. Module was built without symbols.
A first chance exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll
A first chance exception of type 'System.IndexOutOfRangeException' occurred in System.dll

我认为该集合不是线程安全的,您为什么认为它是线程安全的?

此外,您在处理消息时的操作也很可能以某种意想不到的方式改变了集合。

当 1 个线程就可以完成工作时,您希望通过编写复杂的多线程代码来实现什么?

并行循环的每个实例都写入同一个 evlog。所以一个循环正在设置源,而另一个循环已经是 运行.

dictLogs 正在被多个线程同时添加。如果这是一本普通的字典,这会给您带来问题。

问问自己

时会发生什么
dictLogs.Add(ID, new List<string>());

同时调用。

你和

有同样的问题
Logs[logname]++;

同一个条目将被多个线程更新。但是,这不应导致 IndexOutOfRangeException,但会导致计数不正确。

你的ID已经存在异常我想是因为这个

ID = lognameWithoutSpaces + "_" + Logs[logname].ToString();

Logs[logname] 可能同时具有相同的计数。因此这将导致输入重复的键

还有

evlog.Source = logname;

可能不会导致任何异常,但它会给你不正确的结果,因为它可能在到达循环之前被另一个线程更改:

foreach (EventLogEntry message in evlog.Entries)

老实说,我会说在你炸毁宇宙之前去掉并行循环。