9

何をするかを行う.NetクラスはありますかManualResetEvent.PulseAll()(存在する場合)?

同じシグナルを待機している一連のスレッドをアトミ​​ックに解放する必要があります。(本来の用途では「糸のスタンピード」は気になりません。)

これを行うために a を使用することはできませんManualResetEvent。たとえば、次のようにします。

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

その後、シグナルを待機しているスレッドはまったく解放されません。

との呼び出しのThread.Sleep(5)間に a を配置すると、待機中のスレッドのすべてではなく一部がすべて解放されます。スリープを 10 ミリ秒に増やすと、すべてのスレッドを解放できます。(これは 20 スレッドでテストされました。)Set()Reset()

明らかにThread.Sleep()、これを機能させるために追加することは容認できません。

ただし、これは簡単に行うことができる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;
    }
}
4

2 に答える 2

3

Barrierオブジェクトを使用できます。不特定の数のタスクを実行し、他のすべてのタスクがそのポイントに到達するまで待機します。

また、どのコード ブロックからどのタスクが特定の作業単位として機能し始めるかわからない場合は、Go の WaitGroup と同様の方法で使用できます。

于 2013-06-05T22:40:55.930 に答える