c# - 是否有 .Net 类可以执行 ManualResetEvent.PulseAll() 会执行的操作(如果存在)?

标签 c# multithreading

是否有 .Net 类可以执行 ManualResetEvent.PulseAll() 会执行的操作(如果存在)?

我需要自动释放一组等待同一信号的线程。 (对于我的预期用途,我并不担心“线程踩踏”。)

您不能使用 ManualResetEvent 来执行此操作。例如,如果您这样做:

ManualResetEventSlim signal = new ManualResetEventSlim();
// ...
signal.Set();
signal.Reset();

然后根本没有等待信号的线程被释放。

如果您在 Set()Reset() 调用之间放置一个 Thread.Sleep(5),那么除了 并非所有等待的线程都被释放。将 sleep 时间增加到 10 毫秒可以释放所有线程。 (这是用 20 个线程测试的。)

很明显,添加 Thread.Sleep() 来完成这项工作是 Not Acceptable 。

但是,使用 Monitor.PulseAll() 很容易做到这一点,我已经编写了一个小类来完成此操作。 (我编写一个类来执行此操作的原因是我们发现使用 Monitor 的逻辑虽然相当简单,但并不明显,因此值得拥有这样一个类来简化使用。)

我的问题很简单:.Net 中是否已经有类可以执行此操作?

作为引用,这是我的“ManualResetEvent.PulseAll()”等价物的基本版本:

public sealed class Signaller
{
    public void PulseAll()
    {
        lock (_lock)
        {
            Monitor.PulseAll(_lock);
        }
    }

    public void Wait()
    {
        Wait(Timeout.Infinite);
    }

    public bool Wait(int timeoutMilliseconds)
    {
        lock (_lock)
        {
            return Monitor.Wait(_lock, timeoutMilliseconds);
        }
    }

    private readonly object _lock = new object();
}

这是一个示例程序,演示了如果您不在 Set() 和 Reset() 之间休眠,则不会释放任何等待线程:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            _startCounter = new CountdownEvent(NUM_THREADS);

            for (int i = 0; i < NUM_THREADS; ++i)
            {
                int id = i;
                Task.Factory.StartNew(() => test(id));
            }

            Console.WriteLine("Waiting for " + NUM_THREADS + " threads to start");
            _startCounter.Wait(); // Wait for all threads to have started.
            Thread.Sleep(100);
            Console.WriteLine("Threads all started. Setting signal now.");
            _signal.Set();
            // Thread.Sleep(5); // With no sleep at all, NO threads receive the signal. Try commenting this line out.
            _signal.Reset();
            Thread.Sleep(1000);
            Console.WriteLine("\n{0}/{1} threads received the signal.\n\n", _signalledCount, NUM_THREADS);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static void test(int id)
        {
            _startCounter.Signal(); // Used so main thread knows when all threads have started.
            _signal.Wait();
            Interlocked.Increment(ref _signalledCount);
            Console.WriteLine("Task " + id + " received the signal.");
        }

        private const int NUM_THREADS = 20;

        private static readonly ManualResetEventSlim _signal = new ManualResetEventSlim();
        private static CountdownEvent _startCounter;
        private static int _signalledCount;
    }
}

最佳答案

您可以使用 Barrier目的。它允许运行未指定数量的任务,然后等待所有其他任务到达该点。

而且你可以使用它in a way similar to WaitGroup in Go如果您不知道哪些代码块中的哪些任务将作为特定工作单元开始工作。

关于c# - 是否有 .Net 类可以执行 ManualResetEvent.PulseAll() 会执行的操作(如果存在)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15065343/

相关文章:

c++ - valgrind 检查简单的多线程 c++11 得到 sig11 错误

c# - 仅适用于 GET 方法的全局查询过滤器

c# - WCF 代理行为和参数

c# - 如何获取设备名称?

c++ - 调用外部脚本时的奇怪行为

java - 同步方法和 block 之间有什么区别?

c++ - 局部静态指针变量不是线程安全的?

线程内的 Python DBusGMainLoop 和 try and catch block

c# - 当 WPF 中的大量 UI 更新时,ProgressRing 卡住了?

c# - 无法访问该文件,因为该文件正在被另一个使用 ffmpeg 的进程使用