我对某些代码有Parallel.ForEach
,但它给出了与非并行代码不同的结果。因此,出于诊断目的,我使用lock
关键字包装整个代码:
var someArray = new double[123];
var syncObject = new Object();
Parallel.ForEach (windows, (win) =>
{
lock (syncObject) // now it should execute sequentially?
{
// do something with someArray
}
});
我认为锁会使它不平行,但我仍然遇到问题。 我认为它的行为与:
相同windows.ToList().ForEach ( (win) =>
{
// do something with someArray
});
在这种情况下锁怎么可能不会杀死并行性?
答案 0 :(得分:1)
关于您已粘贴的代码段的代码的串行执行,您的假设不正确。
Parallel.ForEach (windows, (win) =>
{
lock (syncObject) // now it should execute sequentially?
{
// do something with someArray
}
});
您放置的lock
确保一次只有一个线程可以访问代码的这个特定关键部分(lock(syncObject) {}
中包含的代码,但并不意味着语句本身将按顺序执行。
用ThreadPool替换你的Parallel.ForEach,可能会让它更容易理解:
foreach(var item in list)
{
ThreadPool.QueueUserWorkItem(i =>
{
lock (syncObject)
{
// do something with i here
}
}, item);
}
这两个片段或多或少相同。如您所见,首先为列表中的每个项启动一个线程,然后在线程内部获取锁,这将确保没有其他线程可以访问该封闭的临界区。然而,这并不能保证它们是按顺序完成的,并且保留了顺序。
线程池中线程的执行顺序超出了您的控制范围,因此,不可能保证使用线程池进行任何排序(至少不是传统意义上的。)
现在让我们来看看这个让希望更清晰的例子:
var syncObject = new Object();
var list = new List<int>();
for(int i=0;i<20;i++)
{
list.Add(i);
}
Parallel.ForEach(list, item =>
{
Console.WriteLine(item + " waiting to be executed on " + Thread.CurrentThread.ManagedThreadId);
lock (syncObject) // now it should execute sequentially?
{
Console.WriteLine(item + " executing on " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
});
此执行的结果与此类似:
0 waiting to be executed on 1
0 executing on 1
2 waiting to be executed on 4
4 waiting to be executed on 6
10 waiting to be executed on 9
12 waiting to be executed on 5
8 waiting to be executed on 10
16 waiting to be executed on 7
6 waiting to be executed on 8
14 waiting to be executed on 3
1 waiting to be executed on 11
2 executing on 4
10 executing on 9
3 waiting to be executed on 4
11 waiting to be executed on 9
16 executing on 7
14 executing on 3
17 waiting to be executed on 7
15 waiting to be executed on 3
8 executing on 10
9 waiting to be executed on 10
4 executing on 6
12 executing on 5
5 waiting to be executed on 6
6 executing on 8
13 waiting to be executed on 5
1 executing on 1
7 waiting to be executed on 8
3 executing on 4
18 waiting to be executed on 1
11 executing on 9
17 executing on 7
15 executing on 3
9 executing on 10
5 executing on 6
13 executing on 5
7 executing on 8
18 executing on 1
19 waiting to be executed on 1
19 executing on 1
正如您所看到的,可能有多个线程等待进入临界区,但在任何给定时间只有一个线程将执行lock
内的语句。 但是由于ThreadPool线程管理和调度的性质,执行的顺序是随机的而不是顺序的。
答案 1 :(得分:0)
将您的代码重写为PLINQ,相当于您的Parallel.ForEach
:
windows
.AsParallel()
.ForAll((win) =>
{
//DoSomething
});
然后将WithDegreeOfParallelism(1)
添加到“KILL”并行度。因此,您可以看到您的错误来自平行运行的线程或来自其他东西。
windows
.AsParallel()
.WithDegreeOfParallelism(1)
.ForAll((win) =>
{
//DoSomething
});