Parallel.For和ConcurrentBag给出了不可预测的行为

wvn*_*wvn 5 c# multithreading

我正在编写一个应用程序,该应用程序有时需要一个网格物体并计算相邻索引。为此,我定义了一个ConcurrentBag对象数组,然后在并行的for循环中,我只检查了一些面孔,如果它们具有邻接关系,则在适当的索引中将索引添加到上述bag中。即:

private bool parallelize = true;
private volatile ConcurrentBag<int>[] edge_adjacencies;
if (parallelize)
{
    ...
    Parallel.For(0, face_count, compute_adjacency_single);
    ...
}

private void compute_adjacency_single(int cur_idx)
{
    edge_adjacencies[cur_idx] = new ConcurrentBag<int>();
    foreach(int test_idx in SOME_TEST_SPACE)
    {
        if (test_idx != cur_idx)
        {
            bool edge_adj, vertex_adj;
            get_adjacency(cur_idx, test_idx, out edge_adj, out vertex_adj);
            if (edge_adj && !collection_contains(edge_adjacencies[cur_idx], test_idx))
            {
                edge_adjacencies[cur_idx].Add(test_idx);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我对集合进行索引,并检查每个集合的大小是否为3(它们都应完全为3):

//DEBUGGING
for (int i = 0; i < face_count; i++)
{
    ConcurrentBag<int> cur = edge_adjacencies[i];
    if (cur.Count != 3) Console.WriteLine("incorrect:" + i);
}
//DEBUGGING
Run Code Online (Sandbox Code Playgroud)

此过程的结果是不可预测的:有时我根本没有输出(所有大小均为3),有时我得到了错误的输出:

运行1:

incorrect:3791
incorrect:3792
incorrect:3829
incorrect:3837
incorrect:4476
Run Code Online (Sandbox Code Playgroud)

运行5:

incorrect:2855
incorrect:2856
incorrect:2879
incorrect:2880
Run Code Online (Sandbox Code Playgroud)

运行8:

incorrect:3271
Run Code Online (Sandbox Code Playgroud)

每9个运行一次给出不正确的结果。

作为参考,当我串行运行时,每次都可以完美运行。

我阅读了MS文档,并确实说System.Collections.Concurrent中的集合应该是线程安全的,但事实并非如此。

为什么会发生这种情况,有什么好方法可以防止这种情况发生?

Jen*_*ter 0

唔。这是一个猜测 - 但我不认为这样做edge_adjacencies[cur_idx] = ..compute_adjacency_single线程安全的。

CuncurrentBag<int>非常线程安全的,但保存它们实例的数组却不是。

我会使用 ConcurrentDictonary而不是数组。