2

複数の「リクエスト スレッド」と 1 つのダムの「ワーカー スレッド」がある状況では、リクエスト スレッドをキューに入れる必要があります。

次の 2 つの可能性を検討してください。

  1. 各要求スレッドは、専用のオブジェクトで Monitor.Wait を呼び出します。このオブジェクトは FIFO キューに入ります。結果が到着すると、最も古いオブジェクトがパルスされます。

  2. すべての要求スレッドは番号を受け取り、共有オブジェクトで Monitor.Wait を呼び出します。結果が到着すると、Monitor.PulseAll が共有オブジェクトで呼び出され、すべての要求スレッドが番号がアップしているかどうかを確認します。

他にも選択肢があるかもしれませんが、この質問の目的では無視してください。

質問 -キューイング スレッドが多数ある場合:

  • どちらの方法でも CPU 効率が大幅に向上しますか?
  • どちらの方法でもメモリ効率が大幅に向上しますか?

ロック オブジェクトは単なる 'new object()' インスタンスです。

私の直感では、シナリオ 1 の方が効率的です。パルスが発生したときに 1 つのスレッドだけが動作し、ベース オブジェクト インスタンスは非常にリソースが少ない (そうですか?) ためです。しかし、Wait の仕組みがよくわかりません。より多くのオブジェクトが「監視」されている場合、より多くのリソースが必要になるのでしょうか?

あなたの洞察を前もって感謝します。


2 つのシナリオを説明するために、以下のコードを記述しました。

さらなる説明:

私の状況では、「ワーカー」スレッドが作業を受け入れ、非同期的に結果を生成します。結果が常に要求が受信されたのと同じ順序で生成されることを除いて、どの要求結果が属するかはわかりません。

私はこれに対する申請書を持っていますが、この質問は学術的なものとして扱われるべきです。根底にある仮定に疑問を投げかけたり、別の解決策を提案したりして時間を無駄にしないでください。ただし、質問の意図を明確にするための質問は大歓迎です。

using System;
using System.Collections.Generic;
using System.Threading;

namespace praccmd.threads
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            TestResets();

            Console.WriteLine("press key");
            Console.ReadKey();
        }


        private static void TestResets()
        {
            //lock object per work request
            Console.WriteLine("----lock object per work request----");

            for (int i = 1; i <= 10; i++)
            {
                Thread t = new Thread(ThreadLockObjPerRequest);
                t.Name = "Thread_object_per_request_" + i;
                t.Start();
            }

            //now pretend to be the WorkDone event
            while (_ticketQueue.Count > 0)
            {
                Thread.Sleep(50);
                lock (_receiveLock)
                {
                    var doneTicketNext = _ticketQueue.Dequeue();
                    lock (doneTicketNext)
                    {
                        Monitor.Pulse(doneTicketNext);
                        Monitor.Wait(doneTicketNext);
                    }
                }
            }

            //shared lock object (pulseall), one id per request
            Console.WriteLine("----shared lock object----");

            for (int i = 1; i <= 10; i++)
            {
                Thread t = new Thread(ThreadSharedLock);
                t.Name = "Thread_shared_lock_object_" + i;
                t.Start();
            }

            //now pretend to be the WorkDone event
            while (_ticketNumberQueue.Count > 0)
            {
                Thread.Sleep(50);
                lock (_sharedReceiveLock)
                {
                    lock (_sharedLock)
                    {
                        _sharedLock.TicketNumber = _ticketNumberQueue.Dequeue();
                        Monitor.PulseAll(_sharedLock);
                    }
                    lock (_sharedThanksLock) Monitor.Wait(_sharedThanksLock);
                }
            }
        }


        //infrastructure for lock-object-per-request
        private static readonly object _sendLock = new object();
        private static readonly object _receiveLock = new object();
        private static readonly Queue<object> _ticketQueue = new Queue<object>();

        private static object TakeATicket()
        {
            var ticket = new object();
            _ticketQueue.Enqueue(ticket);
            return ticket;
        }

        //lock-object-per-request thread
        private static void ThreadLockObjPerRequest()
        {
            var name = Thread.CurrentThread.Name;

            object ticket;
            lock (_sendLock)
            {
                ticket = TakeATicket();
                //RequestWorkNonBlocking("some data specific to this request");
                Console.WriteLine(name + " sends its request.");
            }

            var myResult = string.Empty;
            lock (ticket)
            {
                Monitor.Wait(ticket);
                //myResult = GetResultFromAStaticVariable();
                Console.WriteLine(name + " gets its data.");
                Monitor.Pulse(ticket);
            }

            //do something with myResult
        }


        //infrastructure for shared-lock
        private class SharedLock { public int TicketNumber { get; set; } }

        private static readonly SharedLock _sharedLock = new SharedLock { TicketNumber = 0 };
        private static readonly dynamic _sharedReceiveLock = new object();
        private static readonly dynamic _sharedThanksLock = new object();
        private static readonly object _ticketIncrementLock = new object();
        private static int _ticketNumber = 0;
        private static readonly Queue<int> _ticketNumberQueue = new Queue<int>();

        private static int TakeATicketNumber()
        {
            lock (_ticketIncrementLock)
            {
                _ticketNumberQueue.Enqueue(++_ticketNumber);
                return _ticketNumber;
            }
        }

        //thread for shared-lock
        private static void ThreadSharedLock()
        {
            var name = Thread.CurrentThread.Name;

            int ticketNumber;
            lock (_sendLock)
            {
                ticketNumber = TakeATicketNumber();
                //RequestWorkNonBlocking("some data specific to this request");
                Console.WriteLine(name + " sends its request.");
            }

            var myResult = string.Empty;
            do
            {
                lock (_sharedLock)
                {
                    Monitor.Wait(_sharedLock);
                    if (_sharedLock.TicketNumber == ticketNumber)
                    {
                        myResult = "response"; //GetResultFromAStaticVariable();
                        Console.WriteLine(name + " gets its data.");
                    }
                }
            } while (myResult.Length == 0);

            lock (_sharedThanksLock) Monitor.Pulse(_sharedThanksLock);

            //do something with myResult
        }

    }
}
4

1 に答える 1