Paraller.ForEach 对我不起作用
Paraller.ForEach does not work for me
我有一些代码 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
});
在这种情况下,锁怎么可能不破坏并行性?
将您的代码重写为 PLINQ,这等同于您的 Parallel.ForEach
:
windows
.AsParallel()
.ForAll((win) =>
{
//DoSomething
});
然后将WithDegreeOfParallelism(1)
添加到"KILL"并行度。所以你可以看到你的错误是来自并行 运行 线程还是来自其他东西。
windows
.AsParallel()
.WithDegreeOfParallelism(1)
.ForAll((win) =>
{
//DoSomething
});
关于连续执行您粘贴的代码段的代码,您的假设不正确。
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 线程管理和调度的性质,执行顺序是随机的而不是顺序的。
我有一些代码 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
});
在这种情况下,锁怎么可能不破坏并行性?
将您的代码重写为 PLINQ,这等同于您的 Parallel.ForEach
:
windows
.AsParallel()
.ForAll((win) =>
{
//DoSomething
});
然后将WithDegreeOfParallelism(1)
添加到"KILL"并行度。所以你可以看到你的错误是来自并行 运行 线程还是来自其他东西。
windows
.AsParallel()
.WithDegreeOfParallelism(1)
.ForAll((win) =>
{
//DoSomething
});
关于连续执行您粘贴的代码段的代码,您的假设不正确。
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 线程管理和调度的性质,执行顺序是随机的而不是顺序的。