简体   繁体   English

ThreadPool.QueueUserWorkItem与Parallel.For

[英]ThreadPool.QueueUserWorkItem vs Parallel.For

I'm trying to understand the differences between Parralel.For and ThreadPool.QueueUserWorkItem . 我试图了解Parralel.ForThreadPool.QueueUserWorkItem之间的区别。

Hardware & Software: 硬件软件:

  • Intel i5 (quad core) 英特尔i5(四核)
  • Windows 7 64bit prof Windows 7 64位教授
  • DotNet 4.5 点网4.5

Case 1 code: ThreadPool 案例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);
}

Output: 输出:

Completed 0 using thread 10 (45.871) 使用线程10完成0(45.871)
Completed 1 using thread 11 (45.875) 使用线程11完成1(45.875)
Completed 2 using thread 12 (45.875) 使用线程12完成2(45.875)
Completed 3 using thread 13 (45.875) 使用线程13(3.45.875)完成了3
Completed 4 using thread 10 (46.869) 使用线程10(46.869)完成4

Case 2 code: Parallel.For 案例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") + ")"); });
  });

Output: 输出:

Completed 0 using thread 10 (16.923) 使用线程10(0.16.923)完成0
Completed 1 using thread 11 (16.925) 使用线程11完成1(16.925)
Completed 2 using thread 12 (16.925) 使用线程12完成2(16.925)
Completed 3 using thread 13 (16.926) 使用线程13(16.926)完成了3
Completed 4 using thread 14 (16.926) 使用线程14完成4(16.926)

Question: 题:

From the results in case 1, it appears that only four threads are active, after which the first free thread is then used to complete the final task. 从案例1的结果来看,似乎只有四个线程处于活动状态,然后使用第一个空闲线程来完成最终任务。 In case 2, it appears five threads are immediately dedicated and execute 'simultaneously'. 在情况2中,似乎有五个线程被立即专用并“同时”执行。

Why does the thread handling for QueueUserWorkItem not use a fifth thread, like the parallel class? 为什么QueueUserWorkItem的线程处理不使用第五个线程,例如并行类?

(ThreadPool.GetAvailableThreads(...) confirms that 1023 worker threads are available). (ThreadPool.GetAvailableThreads(...)确认有1023个辅助线程可用)。

I believe there is a difference between an "active" thread and an "available" thread. 我相信“活动”线程和“可用”线程之间是有区别的。 The threadpool will re-use threads, and if it decides it needs more threads it will begin to move available threads to active threads. 线程池将重新使用线程,如果它决定需要更多线程,它将开始将可用线程移至活动线程。 This can be frustrating if you want to start many things at once, as each available thread may take around 2 seconds to start up. 如果您想一次启动很多东西,这可能会令人沮丧,因为每个可用线程可能需要大约2秒钟才能启动。

MSDN Managed Threadpool MSDN托管线程池

As part of its thread management strategy, the thread pool delays before creating threads. 作为其线程管理策略的一部分,线程池在创建线程之前会延迟。 Therefore, when a number of tasks are queued in a short period of time, there can be a significant delay before all the tasks are started. 因此,当许多任务在短时间内排队时,在启动所有任务之前可能会有很大的延迟。

If you were to run these tasks repeatedly, or add tasks, you should see additional threads become active. 如果要重复运行这些任务或添加任务,则应该看到其他线程处于活动状态。

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

In this case, the ThreadPool min worker threads default is 4 - which explains why only 4 threads are used. 在这种情况下,ThreadPool的最小辅助线程默认值为4-这说明了为什么仅使用4个线程。 The documentation states that ThreadPool may decide whether or not to create more threads once the minimum is reached. 文档指出,一旦达到最小值,ThreadPool可能会决定是否创建更多线程。

When setting the minimum number of worker threads to 5, then 5 threads are created and all tasks complete simultaneously as expected. 将最小工作线程数设置为5时,将创建5个线程,并且所有任务将按预期同时完成。

All parallel tasks are finished in multiple threads, that means, thread is a basic unit for parallel tasks. 所有并行任务都在多个线程中完成,这意味着线程是并行任务的基本单位。 So, I think thread pool is more efficient than TPL. 因此,我认为线程池比TPL更有效。 WHY? 为什么? BECAUSE TPL's default task scheduler is ThreadPoolTaskScheduler: 因为TPL的默认任务计划程序是ThreadPoolTask​​Scheduler:

private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler(); 私有静态只读TaskScheduler s_defaultTaskScheduler = new ThreadPoolTask​​Scheduler();

and let's see ThreadPoolTaskScheduler: 让我们看看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);
    }

Then, let's see threadpool : 然后,让我们看一下threadpool

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

OK...let's see our other choice: 好...让我们看看其他选择:

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

OK..let's dig more: 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");
}

Now, finally the same point we found. 现在,终于我们发现了同一点。 So, which is better, it's your choice. 因此,这更好,这是您的选择。

That's why I never use TPL but instead user threadpool directly. 这就是为什么我从不使用TPL ,而是直接使用用户threadpool的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM