简体   繁体   English

Parallel.ForEach IndexOutOfRangeException

[英]Parallel.ForEach IndexOutOfRangeException

I'm trying to go through my event logs with a Parallel.ForEach. 我正在尝试使用Parallel.ForEach浏览事件日志。 But at random it stops with the error IndexOutOfRangeException on the foreach. 但随机地,它会因foreach上的错误IndexOutOfRangeException而停止。

this one: 这个:

foreach (EventLogEntry message in evlog.Entries)

Sometimes it stops with the first time it goes through the loop and sometimes after already gone through the loop 44 times. 有时它会在第一次循环时停止,有时会在已经循环44次之后停止。

Also I sometimes get the error that the given ID already exists. 我有时也会收到给定ID已经存在的错误。

Here's my full code: 这是我的完整代码:

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();

This is what I see in de console: 这是我在控制台中看到的:

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

dictLogs are being added to at the same time by multiple threads. dictLogs被多个线程同时添加。 If this is a normal dictionary this will give you issues. 如果这是一本普通的字典,这将给您带来问题。

Ask yourself what happens when 问自己何时会发生什么

dictLogs.Add(ID, new List<string>());

is called at the exact same time. 恰好在同一时间被调用。

You have the same kind of issue with 你有同样的问题

Logs[logname]++;

where the same entry will be updated by multiple threads. 其中同一条目将由多个线程更新。 However this should not cause an IndexOutOfRangeException but will cause incorrect counts. 但是,这不应导致IndexOutOfRangeException,但会导致计数错误。

Your ID already exists exception I think is because of this 我认为您的ID已经存在,这是因为

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

Logs[logname] could have the same count at the same time. 日志[logname]可以同时具有相同的计数。 Hence this will cause duplicate keys to be entered 因此,这将导致输入重复的键

Also

evlog.Source = logname;

might not cause any exceptions but it will give you incorrect results because it might be changed by another thread before it reaches the loop: 可能不会导致任何异常,但是会给您不正确的结果,因为它可能在到达循环之前被另一个线程更改:

foreach (EventLogEntry message in evlog.Entries)

In all honesty I would say take out the Parallel loop before you blow up the universe. 老实说,我要说的是在炸毁宇宙之前先消除平行循环。

I expect the collection is not thread safe, what makes you think it is? 我希望该集合不是线程安全的,您怎么看呢?

Also it is very possible that your actions when processing a message changes the collection in some unexpected way. 同样,处理消息时您的操作很有可能会以某种意外的方式更改集合。

What are you trying to achieve by writing complex multi threaded code when 1 thread would do the job? 当1个线程可以完成这项工作时,您想通过编写复杂的多线程代码来实现什么?

Each instance of the parallel loop is writing to the same evlog. 并行循环的每个实例都写入相同的evlog。 So one looping is setting the Source while another loop is already running. 因此,一个循环是在另一个循环已经在运行时设置Source。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM