本文介绍了Parallel.ForEach中的迭代器在多个线程上运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些代码在 Parallel.ForEach 循环中运行.循环中的代码是线程安全的,但是我正在使用的迭代器(具有 yield return 的自定义方法)不是.看来迭代器正在多个线程上运行,这可能会导致问题.

I have some code that I run within a Parallel.ForEach loop. The code in the loop is thread safe, but the iterator that I'm using (a custom method with yield return) is not. It appears that the iterator is being run on multiple threads which could potentially cause issues.

问题的背景

迭代器包含对NHibernate的调用(尽管这完全是偶然的,您将在以后看到),并且当我遇到并行化代码的问题时,我在NHibernate Profiler中查看了一下,看看是否可以对情况进行一些介绍.在此过程的一部分开始,报告在多个线程中使用单个会话可能是一个错误".

The iterator contains calls to NHibernate (althought that's entirely incidental as you'll see later) and as I was having issues parallelising the code I looked in NHibernate Profiler to see what if that could shine some light on the situation. Part way through it started to report "Using a single session in multiple threads is likely a bug".

现在,根据NHibernate Profiler,令人讨厌的代码位于迭代器内部(因此,它没有试图在 Parallel.ForEach 操作中在其他地方实现).因此,我添加了一些自己的代码,以便可以检测到什么是NHibernate Profiler,并且看到了相同的东西.

Now, according to NHibernate Profiler the offending code was inside the iterator (so it wasn't trying to materialise something elsewhere in the Parallel.ForEach action). So I added some of my own code so that I could detect what NHibernate Profiler was and I saw the same thing.

正在从多个线程中调用iterator方法-我认为这是不可能的,因为其他人似乎也认为,例如其他答案: https://stackoverflow.com/a/26553810/8152

The iterator method is being called from multiple threads - which I thought was impossible, as others seem to think also, e.g. this other SO answer: https://stackoverflow.com/a/26553810/8152

问题的简化演示

为了演示这个问题(不需要我所有与NHibernate等无关的古怪人物),我写了一个简单的控制台应用程序来显示同样的问题:

To demonstrate the issue (without needing all my extraneous gubbins dealing with NHibernate, etc.) I wrote a simple console app that shows the same problem:

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 (睡眠)模拟我的动作所花费的时间.

Of my test runs, the thread switches between 157 and 174 times in the 1000 iterations. The Sleep simulates the time my action takes.

摘要

如果.NET中实现的迭代器模式本质上不是线程安全的,为什么 Parallel.ForEach 会这样做?和;能够以一种安全的方式(在一个线程上)获取迭代器当前公开的数据,然后在多个线程上对其进行处理,将是一个好的解决方案吗?(例如,是否有任何方法可以将迭代器强制返回到一个线程上?或者迭代器还必须是线程安全的,以及每次迭代调用的操作是什么?还是完全采用其他解决方案?)

Why does Parallel.ForEach do this if the iterator pattern as implemented in .NET is inherently not thread-safe? and; What would be a good solution to be able to get the data, that the iterator currently exposes, in a safe way (on one thread), yet process it on multiple threads? (e.g. Is there any way to force the iterator back onto one thread? Or must the iterator also be thread-safe as well as the action that is invoked for each iteration? Or some other solution entirely?)

版本历史记录

  • 更新了摘要,以希望避免或减少我原来的问题中的XY问题.

推荐答案

否,您必须显式处理多个线程正在调用迭代器的情况.在后台,如果有多个线程正在调用 IEnumerator.MoveNext(),则迭代器将继续前进,其速度与所告知的一样.这里没有隐式同步发生.

No, you have to explicitly handle the condition where multiple threads are calling the iterator. Behind the scenes, if multiple threads are calling IEnumerator.MoveNext(), the iterator will keep advancing as much as it's told to. There is no implicit synchronization happening here.

@JonSkeet不久前在博客中发帖有关锁定迭代.尽管我必须说这看起来像XY问题,但您是否应该从多个线程并行调用 NHibernate ?它是上下文线程安全的吗?在使用线程安全迭代器之前,您应该考虑这些问题.

@JonSkeet blogged a while back about iterating with locking. Although I must say this looks like the XY problem to me, should you be calling NHibernate in parallel from multiple threads? Is it's context thread-safe at all? Those are questions you should consider before going into the wild with thread-safe iterators.

这篇关于Parallel.ForEach中的迭代器在多个线程上运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 15:49