我使用的是并行foreach/for循环,在特殊情况下,我需要使用嵌套的并行foreach/for循环。当我试图打印集合中的值时,有时控制台语句没有打印出来,这是不一致的。请参阅下面的代码片段。
Parallel.For(0, RunModuleConfigVariables.Count, new ParallelOptions { MaxDegreeOfParallelism = 3 }, index => {
string log = null;
int count = 0;
log += "Module Name " + RunModuleConfigVariables.Keys.ElementAt(index) + " thread: " + Thread.CurrentThread.ManagedThreadId + "\n";
Parallel.ForEach(RunModuleConfigVariables[RunModuleConfigVariables.Keys.ElementAt(index)], new ParallelOptions { MaxDegreeOfParallelism = 10 }, eachendpoint => {
log += "\t" + count + " Endpoint Name " + eachendpoint + "\n";
count++;
});
Console.WriteLine(log);
});
字符串
收藏:
集合类型为 ConcurrentDictionary<string,HashSet>()
RunModuleConfigVariables:
{
"Module_1": [
"Module_1_Endpoint_1",
"Module_1_Endpoint_2",
"Module_1_Endpoint_3",
"Module_1_Endpoint_4",
"Module_1_Endpoint_5",
"Module_1_Endpoint_6",
"Module_1_Endpoint_7",
"Module_1_Endpoint_8",
"Module_1_Endpoint_9",
"Module_1_Endpoint_10",
"Module_1_Endpoint_11",
"Module_1_Endpoint_12",
"Module_1_Endpoint_13",
"Module_1_Endpoint_14",
"Module_1_Endpoint_15",
"Module_1_Endpoint_16",
"Module_1_Endpoint_17",
"Module_1_Endpoint_18",
"Module_1_Endpoint_19"
],
"Module_2": [
"Module_2_Endpoint_1",
"Module_2_Endpoint_2",
"Module_2_Endpoint_3"
],
"Module_3": [
"Module_3_Endpoint_1"
]
}
型
实际产量:
Module Name Module_1 thread: 4
0 Endpoint Name Module_1_Endpoint_2
1 Endpoint Name Module_1_Endpoint_1
2 Endpoint Name Module_1_Endpoint_4
3 Endpoint Name Module_1_Endpoint_5
4 Endpoint Name Module_1_Endpoint_6
5 Endpoint Name Module_1_Endpoint_7
6 Endpoint Name Module_1_Endpoint_8
18 Endpoint Name Module_1_Endpoint_9
Module Name Module_3 thread: 5
0 Endpoint Name Module_3_Endpoint_1
Module Name Module_2 thread: 1
0 Endpoint Name Module_2_Endpoint_2
1 Endpoint Name Module_2_Endpoint_3
2 Endpoint Name Module_2_Endpoint_1
型
预期输出:(顺序不必相同)
Module Name Module_1 thread: 5
0 Endpoint Name Module_1_Endpoint_2
1 Endpoint Name Module_1_Endpoint_3
2 Endpoint Name Module_1_Endpoint_4
3 Endpoint Name Module_1_Endpoint_5
4 Endpoint Name Module_1_Endpoint_6
5 Endpoint Name Module_1_Endpoint_7
6 Endpoint Name Module_1_Endpoint_8
7 Endpoint Name Module_1_Endpoint_9
8 Endpoint Name Module_1_Endpoint_10
9 Endpoint Name Module_1_Endpoint_11
10 Endpoint Name Module_1_Endpoint_12
11 Endpoint Name Module_1_Endpoint_13
12 Endpoint Name Module_1_Endpoint_14
13 Endpoint Name Module_1_Endpoint_15
14 Endpoint Name Module_1_Endpoint_16
15 Endpoint Name Module_1_Endpoint_17
16 Endpoint Name Module_1_Endpoint_18
17 Endpoint Name Module_1_Endpoint_19
18 Endpoint Name Module_1_Endpoint_1
Module Name Module_2 thread: 4
0 Endpoint Name Module_2_Endpoint_2
1 Endpoint Name Module_2_Endpoint_3
2 Endpoint Name Module_2_Endpoint_1
Module Name Module_3 thread: 1
0 Endpoint Name Module_3_Endpoint_1
型
注:输出不一致。有时能看到所有的子孩子,有时不能。我该如何理解这一点,以及如何克服这一点?
2条答案
按热度按时间euoag5mw1#
我怎么能理解呢?
并行处理意味着多个线程同时做事情。这会导致各种奇怪的事情,你必须小心。
考虑这一行:
字符串
这一条C#指令实际上代表了多个操作:
1.将
count
变量的值从内存加载到处理器中。1.将
1
添加到加载到处理器中的值。1.将新值存储到
count
变量的内存位置。现在想象两个线程同时执行这三条指令。有一个微小的可能性,他们都将完成步骤1之前,任何完成步骤3。这意味着如果
count
从零开始,两个线程现在都会将count
设置为1
,这不是您想要的。这一行在读取
log
的点和写入log
的点之间有更多的步骤:型
因此,您会发现一个线程更频繁地覆盖(而不是添加)另一个线程已经写入的值。这就是你注意到的行为。
......让我知道,可以做些什么来克服这一点。
首先,尽可能避免并行处理。
如果使用简单的
foreach
循环就能让事情变得足够快,那么就不要试图优化它们。如果使用简单的
foreach
循环运行速度不够快,请找出原因。大多数情况下,这是由于I/O操作(磁盘或网络访问)。在这些情况下,使用异步任务的并发执行,而不是多线程。参见https://stackoverflow.com/a/14130314/120955和What is the difference between asynchronous programming and multithreading?。如果您正在执行需要CPU能力的操作,并且您确实需要它们并行运行以挤出额外的性能,请尝试避免在每个操作中更改状态(例如:设置共享变量的值,如
count++
)。一个很好的策略是命令/查询分离,在这里你对不可变的数据结构进行并行处理以产生“答案”,然后使用这些答案来进行必须在同一个线程上进行的更改。下面是它在代码中的样子:型
最后,如果你必须并行地改变状态,你需要花时间学习锁、互斥锁、并发集合、atomic operations和其他类似的工具,并确保你只在并行上下文中使用线程安全的方法,以确保你做的是“正确的”。
可能会导致这样的结果:
型
rkue9o1l2#
问题是变量
log
被多个线程分配给了。在尝试写入之前,您需要lock
。字符串