2

最後に編集は次のとおりです。

2 つの異なるプロセスをセマフォと同期しています。

var semaphore = new Semaphore(0, 1, "semName");

したがって、プロセスAとプロセスBがあり、たまたま異なる実行可能ファイルです。

プロセスA

    static void Main(string[] args)
    {

        Console.Write("Process A");
        Task.Factory.StartNew(() =>
        {
            var semaphoreName = "sem";
            var semaphore = Semaphore.OpenExisting(semaphoreName);

            Thread.Sleep(100);

            semaphore.Release();

            semaphore.WaitOne();

            semaphore.Release();

            Console.Write("Process A Completed!");
        });
        Console.Read();

    }

プロセス B (別のコンソール アプリ)

    static void Main(string[] args)
    {
        Console.Write("Process B");
        Task.Factory.StartNew(() =>
        {
            var semaphoreName = "sem";
            var semaphore = new Semaphore(0, 1, semaphoreName);

            semaphore.WaitOne();

            Thread.Sleep(1000);

            semaphore.Release();

            semaphore.WaitOne();

            Console.Write("Process B Completed!");
        });

        Console.Read();


    }

プロセスをデバッグするか、配置するThread.Sleepと、最後の行に到達できます。Console.Write("Process A Completed!"); 配置せずにこの問題を解決するにはどうすればよいThread.Sleep(100);ですか?




編集

競合状態はありません!! たぶん私が間違っているのなら、私を訂正してください。とにかく、競合状態がないと思う理由は次のとおりです。

プロセスA

    static void Main(string[] args)
    {
        Console.Write("Process A");
        var semaphoreName = "sem";

        Task.Factory.StartNew(() =>
        {                                
            var semaphore = Semaphore.OpenExisting(semaphoreName);

            semaphore.Release();

            semaphore.WaitOne();

            // this line should never be reached but it is!!!

            Console.Write("Process A Completed!");
        });            

        Console.Read();

    }

プロセスB

    static void Main(string[] args)
    {
        Console.Write("Process B");
        var semaphoreName = "sem";

        Task.Factory.StartNew(() =>
        {                

            var semaphore = new Semaphore(0, 1, semaphoreName);

            semaphore.WaitOne();

            // important to have these lines
            int a = 0;
            for (var i = 0; i < 1000000000; i++)
                a = i;

            Thread.Sleep(10000); // there should not be a race condition any more!!!!

            Console.Write("Process B Completed!");
        });

        Console.Read();
    }

プロセス A では、次の行に到達してはならないことに注意してConsole.Write("Process A Completed!"); ください。

プロセス B は 10000 秒間スリープするため、競合状態が発生する理由はありません。さらに、ループを削除すると、for (var i = 0; i < 1000000000; i++)その動作は得られません。

4

3 に答える 3

2

セマフォを待機しているスレッドは、セマフォを待機し始めるのと同じ順序で必ず解放されることを示すドキュメントを見つけることができませんでした。したがって、工程Aに入る機会を与えずにsem.WaitOne()、工程Bがそのすぐ上のプロセスによって解放されている可能性はありませんか?sem.Release()

プロセス B でセマフォを待機する前に を追加すると、Thread.Sleep()おそらくプロセス A が代わりにセマフォに入る機会が与えられます。

このようにセマフォを使用している理由、またはsem.WaitOne()インプロセス B が何を待っているのか (プロセス A がコード例でセマフォを解放しないため) を完全に明確にしていないため、改善を提案することは困難です。問題を解決する。

ただし、プロセス A がプロセス B の開始を待ってから作業を開始することを意図している場合、プロセス B がプロセス A が作業を完了するのを待ってから続行する場合、2 つManualResetEventの s がより効果的にこれを達成できるようです (具体的にはプロセス A が待機する「プロセス B 準備完了」イベントと、プロセス B が待機する「プロセス A 完了」イベント)。

考えられる解決策:

プロセスA:

    static void Main(string[] args)
    {
        Console.Write("Process A");
        Task.Factory.StartNew(() =>
        {
            var readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "Process B Ready");
            var doneEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "Process A Finished");

            // Wait for process B to be ready...
            readyEvent.WaitOne();

            // Do some work...

            Console.Write("Process A Completed!");

            // Signal that the process is complete
            doneEvent.Set();
        });
        Console.Read();
    }

プロセス B:

    static void Main(string[] args)
    {
        Console.Write("Process B");
        Task.Factory.StartNew(() =>
        {
            var readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "Process B Ready");
            var doneEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "Process A Finished");

            // Signal that process B is ready
            readyEvent.Set();

            // Wait for process A to complete...
            doneEvent.WaitOne();

            Console.Write("Process B Completed!");
        });

        Console.Read();
    }
于 2012-08-10T15:46:01.203 に答える
1

現在、プロセス B はセマフォを解放する前に消費しません。OpenExistingドキュメントによると、カウントを消費しません。WaitOne使用可能なスペースがある場合、セマフォ カウントをインクリメントします。

したがって、A が含まれているため最初に実行し、B が含まれているnew Semaphoreために B を 2 番目に実行するOpenExistingと、A はそれを取得して 4 秒間スリープし、決して解放しません。B はそれを取得する前に解放しようとします。

待ってWaitOneいないということは、他のプロセスとの競合状態のように思えます。10ms により、他のプロセスが最初に入ることができるためWaitOne、2 番目のプロセスではスペースがないために待機しているように見えます。

セマフォは、スペースがない場合にのみブロックします。スペースWaitOneがある場合は、非常に迅速に戻ります。

于 2012-08-10T15:50:05.767 に答える
0

特定の質問に対する一般的な回答として、他の人が指摘したように、コードに競合状態があります。

これが、ほとんどの場合実際に起こっていることであり、なぜそれが体系的であると見なされるのか (実際にはランダムであるにもかかわらず) です。

スレッドを開始します。

  • A が開始され、セマフォでブロックされます。
  • B は、セマフォのロックを開始して解放します。

ここでシナリオが分岐します。

Thread.Sleep(100) があり、Thread.Sleep(10) がない場合、.NET の内部実装はスレッドを一時停止し、プロセス A を開始します。これは、セマフォを最も長く待っているプロセスです。

おそらく Thread.Sleep(10) がある場合 (確かなことはわかりません) 、プロセスは 10 ミリ秒間スピンし、コンテキストの変更を行わずに (つまり、実際にスレッドをスリープ状態にせずに) 続行します。セマフォを再取得するため、プロセス A がロック解除されることはありません。もちろん、thread.sleep がない場合も同じことが起こります。基本的に、 Thread.Sleep(100) を使用すると、コンテキストの切り替えが強制され、プロセス A がスレッドを取得できるようになります。もちろん、コンテキストスイッチがとにかく入る可能性があるため、それは完全にランダムですが、そのように強制すると、言及した結果が表示されます。

編集:更新された質問については、間違っていると考えています。2 つのプロセスまたはスレッドを同期するには、セマフォ (この場合はミューテックス) を使用します。したがって、スレッドの1つにセマフォを取得させ、もう1つのスレッドにセマフォを解放させます。これにより、2番目のスレッドが最初のスレッドに続行できることを「通知」します。

プロセスを両方の方法で機能させたい場合は、2 つのセマフォを使用する必要があります。

于 2012-08-10T15:59:29.893 に答える