2

我试图理解 和 之间的Parralel.For区别ThreadPool.QueueUserWorkItem

硬件软件:

  • Intel i5(四核)
  • Windows 7 64 位教授
  • 点网 4.5

案例一代码:线程池

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 (45.871) 完成 0 使用线程
11 (45.875)
完成 1 使用线程 12 (45.875)
完成 2 使用线程 13 (45.875)
完成 3 使用线程 10 (46.869) 完成 4

案例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 (16.923) 完成 0 使用线程
11 (16.925)
完成 1 使用线程 12 (16.925)
完成 2 使用线程 13 (16.926)
完成 3 使用线程 14 (16.926 ) 完成 4

问题:

从案例 1 的结果来看,似乎只有四个线程处于活动状态,然后使用第一个空闲线程完成最终任务。在情况 2 中,似乎有五个线程立即专用并“同时”执行。

为什么 QueueUserWorkItem 的线程处理不使用第五个线程,如并行类?

(ThreadPool.GetAvailableThreads(...)确认有 1023 个工作线程可用)。

4

3 回答 3

0

我相信“活动”线程和“可用”线程之间存在差异。线程池将重用线程,如果它决定需要更多线程,它将开始将可用线程移动到活动线程。如果您想一次启动许多事情,这可能会令人沮丧,因为每个可用线程可能需要大约 2 秒才能启动。

MSDN 托管线程池

作为其线程管理策略的一部分,线程池在创建线程之前会延迟。因此,当许多任务在短时间内排队时,在所有任务启动之前可能会有很大的延迟。

如果您要重复运行这些任务或添加任务,您应该会看到其他线程处于活动状态。

于 2013-12-19T16:16:53.687 回答
0

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.setminthreads%28v=vs.110%29.aspx

在这种情况下,ThreadPool 最小工作线程默认值为 4 - 这解释了为什么只使用 4 个线程。文档指出,一旦达到最小值,ThreadPool 可能会决定是否创建更多线程。

将最小工作线程数设置为 5 时,会创建 5 个线程,并且所有任务会按预期同时完成。

于 2013-12-20T07:49:32.580 回答
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);
    }
}

好的......让我们看看我们的另一个选择:

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

好的..让我们挖掘更多:

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 回答