2

と の違いを理解しようとしていParralel.ForますThreadPool.QueueUserWorkItem

ハードウェアとソフトウェア:

  • Intel i5 (クアッドコア)
  • ウィンドウズ 7 64 ビット 教授
  • ドットネット 4.5

ケース 1 コード: ThreadPool

for (int index = 0; index < 5; index++)
{
  ThreadPool.QueueUserWorkItem((indexParam) =>
  {
    int threadID = Thread.CurrentThread.ManagedThreadId;
    Thread.Sleep(1000);
    BeginInvoke((Action)delegate { listBox1.Items.Add("Completed " + indexParam.ToString() + " using thread " + threadID.ToString() + "  (" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString("000") + ")"); });
  }, index);
}

出力:

スレッド 10 を使用して 0 を完了 (45.871)
スレッド 11 を使用して 1 を完了 (45.875)
スレッド 12 を使用して 2 を完了 (45.875)
スレッド 13 (45.875) を使用して 3 を
完了

ケース 2 コード: Parallel.For

  ParallelLoopResult result = Parallel.For(0, 5, (int index, ParallelLoopState loopState) =>
  {
    int threadID = Thread.CurrentThread.ManagedThreadId;
    Thread.Sleep(1000);
    BeginInvoke((Action)delegate { listBox1.Items.Add("Completed " + index.ToString() + " using thread " + threadID.ToString() + "  (" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString("000") + ")"); });
  });

出力:

スレッド 10 を使用して 0 を完了 (16.923)
スレッド 11 を使用して 1 を完了 (16.925)
スレッド 12 を使用して 2 を完了 (16.925)
スレッド 13 (16.926) を使用して 3 を
完了

質問:

ケース 1 の結果から、アクティブなスレッドは 4 つだけのように見えます。その後、最初の空きスレッドを使用して最後のタスクを完了します。ケース 2 では、5 つのスレッドがすぐに専用になり、「同時に」実行されるようです。

QueueUserWorkItem のスレッド処理で、並列クラスのように 5 番目のスレッドが使用されないのはなぜですか?

(ThreadPool.GetAvailableThreads(...)1023 個のワーカー スレッドが使用可能であることを確認します)。

4

3 に答える 3

0

すべての並列タスクは複数のスレッドで終了します。つまり、スレッドは並列タスクの基本単位です。したがって、TPL よりもスレッド プールの方が効率的だと思います。なぜ?TPL のデフォルトのタスク スケジューラは ThreadPoolTask​​Scheduler であるため:

プライベート静的読み取り専用 TaskScheduler s_defaultTaskScheduler = new ThreadPoolTask​​Scheduler();

ThreadPoolTask​​Scheduler を見てみましょう:

    protected internal override void QueueTask(Task task)
    {
        if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
        {
            new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)
            {
                IsBackground = true
            }.Start(task);
            return;
        }
        bool forceGlobal = (task.Options & TaskCreationOptions.PreferFairness) != TaskCreationOptions.None;
        ThreadPool.UnsafeQueueCustomWorkItem(task, forceGlobal);
    }

それでは、見てみましょうthreadpool

internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
{
    ThreadPool.EnsureVMInitialized();
    try
    {
    }
    finally
    {
        ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
    }
}

OK...他の選択肢を見てみましょう:

public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, object state)
{
    StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
    return ThreadPool.QueueUserWorkItemHelper(callBack, state, ref stackCrawlMark, false);
}

OK..もっと掘り下げましょう:

private static bool QueueUserWorkItemHelper(WaitCallback callBack, object state, ref StackCrawlMark stackMark, bool compressStack)
{
    bool result = true;
    if (callBack != null)
    {
        ThreadPool.EnsureVMInitialized();
        try
        {
            return result;
        }
        finally
        {
            QueueUserWorkItemCallback callback = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
            ThreadPoolGlobals.workQueue.Enqueue(callback, true);
            result = true;
        }
    }
    throw new ArgumentNullException("WaitCallback");
}

さて、ついに同じポイントを見つけました。だから、どちらが良いかは、あなたの選択です。

そのため、私は決して使用TPLせず、代わりにユーザーthreadpoolを直接使用します。

于 2014-07-09T16:51:13.237 に答える