3

私は再発明による学習を強く信じています。そのような心構えで、私はカスタムスレッドプールの実装に着手しました。私が自分で設定した目標は次のとおりです。

  1. スレッドプールで作業項目をキューに入れることができるようにするため。
  2. 固定数のスレッドで作業項目を処理できるようにするため–すべて同時に作成されます。
  3. 一般的なワーカースレッド関数は、両端キューの方法のみを知っている必要があり、IsEmptyやCountなどの他の関数/プロパティを処理するべきではありません。

私は上記の目的を達成することに成功しましたが、スタックオーバーフローの専門家と一緒に取ったアプローチを検証したいと思います。また、より良いアプローチがあるかどうか、またはマルチスレッドの専門家がこの問題をどのように解決するかを知りたいと思います。次の段落では、私が直面した課題とそれをどのように修正したかについて説明しました。

私が作成したスレッドプールは、すべてのワーカースレッドがアイテムを選択して処理する作業アイテムのキューを内部的に維持していました。新しいアイテムがキューに入れられると、フリースレッドがそれを取得して実行できるようにイベントを通知します。

autoreseteventを開始して、キュー上の新しい作業項目の待機中のスレッドに通知しましたが、通知されたイベントが失われるという問題に直面しました。これは、複数のアイテムがキューに入れられ、アイテムを処理するための空きスレッドがない場合に発生します。未処理のまま残っているアイテムの合計は、セット(シグナリング)イベントが重複しているために失われたシグナルイベントの合計と同じです。

シグナルイベントが失われる問題を修正するために、autoreseteventの上にラッパーを作成し、autoreseteventの代わりに使用しました。問題を修正しました。同じもののコードリストは次のとおりです。

public static class CustomThreadPool
{
    static CustomThreadPool()
    {
        for (int i = 0; i < minThreads; i++)
            _threads.Add(
                new Thread(ThreadFunc) { IsBackground = true }
                );

        _threads.ForEach((t) => t.Start());
    }

    public static void EnqueWork(Action action)
    {
        _concurrentQueue.Enqueue(action);
        _enqueEvent.Set();
    }

    private static void ThreadFunc()
    {
        Action action = null;
        while (true)
        {
            _enqueEvent.WaitOne();
            _concurrentQueue.TryDequeue(out action);
            action();
        }
    }

    private static ConcurrentQueue<Action> _concurrentQueue = new ConcurrentQueue<Action>();
    private static List<Thread> _threads = new List<Thread>();
    private static CountAutoResentEvent _enqueEvent = new CountAutoResentEvent();
    private static object _syncObject = new object();
    private const int minThreads = 4;
    private const int maxThreads = 10;

    public static void Test()
    {
        CustomThreadPool.EnqueWork(() => {

            for (int i = 0; i < 10; i++) Console.WriteLine(i);
            Console.WriteLine("****First*****");
        });

        CustomThreadPool.EnqueWork(() =>
        {
            for (int i = 0; i < 10; i++) Console.WriteLine(i);
            Console.WriteLine("****Second*****");
        });

        CustomThreadPool.EnqueWork(() =>
        {
            for (int i = 0; i < 10; i++) Console.WriteLine(i);
            Console.WriteLine("****Third*****");
        });

        CustomThreadPool.EnqueWork(() =>
        {
            for (int i = 0; i < 10; i++) Console.WriteLine(i);
            Console.WriteLine("****Fourth*****");
        });

        CustomThreadPool.EnqueWork(() =>
        {
            for (int i = 0; i < 10; i++) Console.WriteLine(i);
            Console.WriteLine("****Fifth*****");
        });
    }
}

public class CountAutoResentEvent
{
    public void Set()
    {
        _event.Set();
        lock (_sync)
            _countOfSet++;
    }

    public void WaitOne()
    {
        _event.WaitOne();
        lock (_sync)
        {
            _countOfSet--;
            if (_countOfSet > 0)
                _event.Set();
        }
    }

    private AutoResetEvent _event = new AutoResetEvent(false);
    private int _countOfSet = 0;
    private object _sync = new object();
}

今、私はいくつかの質問があります:

  1. 私のアプローチは完全な証拠ですか?
  2. この問題に最適な同期メカニズムとその理由は何ですか?
  3. マルチスレッドの専門家はこの問題にどのように対処しますか?

ありがとう。

4

1 に答える 1

1

私が見たものから、私はそれが正しいと言うでしょう。私はConcurrentQueueあなたがあなた自身の同期キューを使用し、実装しようとしなかったことを気に入っています。これは混乱であり、既存のものほど速くはない可能性があります。

カスタムの「シグナリングメカニズム」は、実際にはセマフォと非常によく似ています。つまり、複数のスレッドがクリティカルセクションに入ることを許可するロックです。この機能は、 Semaphoreクラスにすでに存在します。

于 2011-12-17T08:59:34.940 に答える