【发布时间】:2016-08-31 15:35:25
【问题描述】:
我有一些在Parallel.ForEach 循环中运行的代码。循环中的代码是线程安全的,但我使用的迭代器(带有yield return 的自定义方法)不是。似乎迭代器正在多个线程上运行,这可能会导致问题。
问题的背景
迭代器包含对 NHibernate 的调用(尽管稍后您将看到这完全是偶然的),并且由于我在并行化代码时遇到问题,我在 NHibernate Profiler 中查看了是否可以对这种情况有所了解。通过它开始报告“在多个线程中使用单个会话可能是一个错误”。
现在,根据 NHibernate Profiler 的说法,有问题的代码位于迭代器内部(因此它并没有试图在 Parallel.ForEach 操作中的其他地方实现某些东西)。所以我添加了一些我自己的代码,这样我就可以检测到 NHibernate Profiler 是什么,我看到了同样的东西。
迭代器方法是从多个线程调用的——我认为这是不可能的,正如其他人似乎也认为的那样,例如这个其他的答案:https://stackoverflow.com/a/26553810/8152
问题的简化演示
为了演示这个问题(不需要我所有无关的 gubbins 处理 NHibernate 等),我编写了一个简单的控制台应用程序来显示相同的问题:
public class Program
{
public static void Main(string[] args)
{
Parallel.ForEach(YieldedNumbers(), (n) => { Thread.Sleep(n); });
Console.WriteLine("Done!");
Console.ReadLine();
}
public static IEnumerable<int> YieldedNumbers()
{
Random rnd = new Random();
int lastKnownThread = Thread.CurrentThread.ManagedThreadId;
int detectedSwitches = 0;
for (int i = 0; i < 1000; i++)
{
int currentThread = Thread.CurrentThread.ManagedThreadId;
if (lastKnownThread != currentThread)
{
detectedSwitches++;
Console.WriteLine(
$"{detectedSwitches}: Last known thread ({lastKnownThread}) is not the same as the current thread ({currentThread}).");
lastKnownThread = currentThread;
}
yield return rnd.Next(100,250);
}
}
}
在我的测试运行中,线程在 1000 次迭代中在 157 和 174 次之间切换。 Sleep 模拟我的操作所花费的时间。
总结
如果.NET 中实现的迭代器模式本质上不是线程安全的,为什么Parallel.ForEach 会这样做?和;什么是能够以安全的方式(在一个线程上)获取迭代器当前公开的数据,但在多个线程上处理它的好解决方案? (例如,有没有办法强制迭代器回到一个线程上?或者迭代器也必须是线程安全的,以及每次迭代调用的操作?或者完全是其他解决方案?)
版本历史
- 更新了摘要,希望能避免或减少我原来问题的 XY 问题。
【问题讨论】:
标签: c# .net multithreading task-parallel-library parallel.foreach