4

マルチスレッドや のパワーを高く評価していないというわけではありませんがThreadPool、比較的単純なThreadPool. だから私はここに私のコードを提出して、私よりもはるかに賢い人々に分解してもらいます。

ここで何か間違ったことをしているのですか、それともこれまで期待していたよりもはるかに優れたマルチスレッドの候補ですか? (はい、この関数はスレッド全体です。先ほど言ったように、実行に 1 分以上かかっていました)

編集:私自身の質問に答えるには、いいえ、これは壊れています:複数回実行されているようですが、同じトリガーで実行されています。これは、ラムダの処理方法によるものですか?

private static void CompileEverything()
{
    try
    {
        // maintain the state of our systray icon
        object iconLock = new object();
        bool iconIsOut = true;

        // keep a count of how many threads are still running
        object runCountLock = new object();
        int threadRunning = 0;

        foreach (World w in Worlds)
        {
            foreach (Trigger t in w.Triggers)
            {
                lock (runCountLock)
                {
                    threadRunning++;
                }

                ThreadPool.QueueUserWorkItem(o =>
                {
                    // [snip]: Do some work involving compiling code already in memory with CSharpCodeProvider

                    // provide some pretty feedback
                    lock (iconLock)
                    {
                        if (iconIsOut)
                            notifyIcon.Icon = Properties.Resources.Icon16in;
                        else
                            notifyIcon.Icon = Properties.Resources.Icon16out;

                        iconIsOut = !iconIsOut;
                    }

                    lock (runCountLock)
                    {
                        threadRunning--;
                    }
                });
            }
        }

        // wait for all the threads to finish up
        while (true)
        {
            lock (runCountLock)
            {
                if (threadRunning == 0)
                    break;
            }
        }

        // set the notification icon to our default icon.
        notifyIcon.Icon = Properties.Resources.Icon16;
    }
    // we're going down before we finished starting...
    // oh well, be nice about it.
    catch (ThreadAbortException) { }
}
4

3 に答える 3

4

Interlocked.Increment はロックよりも優れていますが、最後のポーリング ループが怖いです。まず、ループする場合は、毎回 Thread.Sleep(0) を実行してプロセッサを解放します。次に、変数をポーリングする場合は、変数が volatile としてマークされているか、MemoryBarrier を使用していることを確認する必要があります。そうしないと、コンパイラは、外部スレッドが変数を変更しないと想定し、チェックを最適化して、無限につながる可能性があります。ループ。

さらに良いのは、各スレッドがゼロになったことを確認し、そうであればイベントを設定することです。その後、ポーリングの代わりにイベントを待機できます。唯一の秘訣は、ディスパッチ ループの前にメイン スレッドで 1 回インクリメントしてから、イベントを待機する前にデクリメントしてゼロをチェックすることです。

編集

トリガーを再利用しているために壊れている場合は、クロージャーが間違っています。world の値をループ内のローカル変数にコピーし、その変数をラムダ式に使用しようとしています。

于 2009-07-20T00:04:07.123 に答える
1

私はあなたがもっとうまくやれると思います。threadRunning への変更をロックする必要はありません。Interlocked.Increment() と Interlocked.Decrement() を使用できます。

        Interlocked.Increment(ref threadRunning);
        ThreadPool.QueueUserWorkItem(o =>
        {
            // [snip]: Do some work involving compiling code already in memory with CSharpCodeProvider

            // provide some pretty feedback
            lock (iconLock)
            {
                notifyIcon.Icon = (iconIsOut ? Properties.Resources.Icon16in : Properties.Resources.Icon16out);
                iconIsOut = !iconIsOut;
            }

            Interlocked.Decrement(ref threadRunning);
        });
于 2009-07-19T23:56:32.957 に答える
0

ThreadPool は、実行中のスレッドの数を、最大限に効率的なプロセッサの数に自動的に制限します。各コンテキスト スイッチは、メモリ スワッピングの4Kb ページ単位で最大1Mb (デフォルト)であるため、コアよりも多くのスレッドを使用している場合、コンテキスト スイッチがないだけで大量の速度が得られる可能性があります。

于 2009-07-19T23:54:38.013 に答える