CLR 线程池

CLR 线程池: CLR初始化时线程池是怎样的,在什么状况下建立,什么状况下收回,销毁。算法

  • 线程池在CLR中,CLR中的多个AppDomain共享这个线程池。编程

  • CLR在初始化时线程池中没有线程。windows

  • 线程池内部维护了一个操做请求队列。数组

  • 调用线程池某个方法,将一个记录项(entry)追加到线程池队列中。安全

  • 线程池的内部代码将记录项派发给一个线程池线程。并发

  • 若是线程池中没有就建立一个,线程执行完后,再收回到线程池中。async

  • 若是应用程序向线程池发了不少请求,一个线程忙不过来就会再建立更多的线程。ide

  • 当一个线程池线程闲着没事一段时间后,线程会本身醒来终止本身以释放资源。oop

 

ThreadPool 的使用:性能

using System;
using System.Threading;
public static class Program {
public static void Main() {
       Console.WriteLine("Main thread: queuing an asynchronous operation");
       ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5);
       Console.WriteLine("Main thread: Doing other work here...");
       Thread.Sleep(10000); // Simulating other work (10 seconds)
       Console.WriteLine("Hit <Enter> to end this program...");
       Console.ReadLine();
  }
   // This method's signature must match the WaitCallback delegate
   private static void ComputeBoundOp(Object state) {
       // This method is executed by a thread pool thread
       Console.WriteLine("In ComputeBoundOp: state={0}", state);
       Thread.Sleep(1000); // Simulates other work (1 second)
       // When this method returns, the thread goes back
       // to the pool and waits for another task
  }
}

 

执行上下文

执行上下文 是每一个线程都有的,它包含了:安全设置、宿主设置、 以及逻辑调用上下文数据。执行上下文设置 会影响线程执行它的代码。那是怎么影响的呢?

当一个线程(主)使用另外一个线程(辅)执行任务时,前者的执行上下文应该流向(复制)辅助线程。

确保辅助线程的操做 和主线程是相同的安全设置和宿主设置。可是主线程将 上下文流向 辅助线程 这个操做很耗时间。

System.Threading 命名空间中的ExecutionContext 类,能够控制主线程 如何将执行上下文流向另外一个辅助线程。

    public sealed class ExecutionContext : IDisposable, ISerializable {
  [SecurityCritical] public static AsyncFlowControl SuppressFlow();
   public static void RestoreFlow();
   public static Boolean IsFlowSuppressed();
   // Less commonly used methods are not shown
}

它能够阻止执行上下文流动以提高应用程序的性能。

public static void Main() {
   // Put some data into the Main thread's logical call context
   CallContext.LogicalSetData("Name", "Jeffrey");
   // Initiate some work to be done by a thread pool thread
   // The thread pool thread can access the logical call context data
   ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));
   // Now, suppress the flowing of the Main thread's execution context
   ExecutionContext.SuppressFlow();
   // Initiate some work to be done by a thread pool thread
   // The thread pool thread CANNOT access the logical call context data
   ThreadPool.QueueUserWorkItem(state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));
   // Restore the flowing of the Main thread's execution context in case
   // it employs more thread pool threads in the future
   ExecutionContext.RestoreFlow();
  ...
   Console.ReadLine();
}

当编译完成后运行的结果以下:

Name=Jeffrey
Name=

 

协做式取消和超时

  • 取消操做首先要建立一个 System.Threading.CancellationTokenSource 对象。这个对象包含了和管理取消有关的全部状态。

  • CancellationTokenSource 对象的 Token 属性 得到 一个或多个 CancellationToken 实例,传给你的操做就能够取消。

对一个任务的取消操做的例子以下:

public struct CancellationToken { // A value type
   public static CancellationToken None { get; } // Very convenient
   public Boolean IsCancellationRequested { get; } // Called by non•Task invoked operations
   public void ThrowIfCancellationRequested(); // Called by Task•invoked operations
   // WaitHandle is signaled when the CancellationTokenSource is canceled
   public WaitHandle WaitHandle { get; }
   // GetHashCode, Equals, operator== and operator!= members are not shown
   public Boolean CanBeCanceled { get; } // Rarely used
   public CancellationTokenRegistration Register(Action<Object> callback, Object state, Boolean useSynchronizationContext); // Simpler overloads not shown
}

 

任务

ThreadPool 的 QueueUserWorkItem 有许多限制,没有内建的机制让你知道操做在何时完成,也没有机制在操做完成时得到返回值。task 任务能够替代ThreadPool。

ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5); // Calling QueueUserWorkItem
new Task(ComputeBoundOp, 5).Start(); // Equivalent of preceding using Task
Task.Run(() => ComputeBoundOp(5)); // Another equivalent

为了建立一个Task,须要调用构造器并传递一个Action或Action<Object>委托。这个委托就是你想要执行的操做。

能够向构造器传递一些TaskCreationOptions 标志类控制Task 的执行方式。TaskCreationOptions 枚举类型定义了一组可按位OR 的标志,定义以下:

[Flags, Serializable]
public enum TaskCreationOptions {
   None = 0x0000,// The default
   // Hints to the TaskScheduler that you want this task to run sooner than later.
   PreferFairness = 0x0001,
   // Hints to the TaskScheduler that it should more aggressively create thread pool threads.
   LongRunning = 0x0002,
   // Always honored: Associates a Task with its parent Task (discussed shortly)
   AttachedToParent = 0x0004,
   // If a task attempts to attach to this parent task, it is a normal task, not a child task.
   DenyChildAttach = 0x0008,
   // Forces child tasks to use the default scheduler as opposed to the parent’s scheduler.
   HideScheduler = 0x0010
}

 

等待任务完成并获取结果

private static Int32 Sum(Int32 n) {
   Int32 sum = 0;
   for (; n > 0; n--)
   checked { sum += n; } // if n is large, this will throw System.OverflowException
   return sum;
}

如今构造一个Task<TResult> 对象,并为泛型TResult 参数传递计算限制操做的返回类型。开始任务以后,可等待它完成并得到结果。

// Create a Task (it does not start running now)
Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000000000);
// You can start the task sometime later
t.Start();
// Optionally, you can explicitly wait for the task to complete
t.Wait(); // FYI: Overloads exist accepting timeout/CancellationToken
// You can get the result (the Result property internally calls Wait)
Console.WriteLine("The Sum is: " + t.Result); // An Int32 value
  • 调用 Wait 方法 或者 Result属性时,这些成员会抛出一个System.AggergateException 对象。

  • AggregateException 类型封装了异常对象的一个集合,该类的InnerExceptions属性包含一个异常列表。

  • 若是一直不调用Wait 或 Result ,或者一直不查询Task 的 Exception 属性,代码就一直注意不到这个异常的发生。

  • 为了帮你检测到该异常,能够向 TaskerScheduler 的静态 UnobservedTaskException 事件登记一个回调方法。

  • 每次当一个Task 被垃圾回收时,若是存在一个没有被注意到的异常,CLR 的终结器线程就会引起这个事件。

  • 一旦引起就会向你注册的事件处理方法中传递一个UnobservedTaskExceptionEventArgs对象,这其中包含你没有注意到的AggregateException。

  • Task 的两个静态方法 WaitAny 和 WaitALl ,它们都会阻塞调用线程,直到数组中全部的Task 对象完成。均可以经过CancellationToken 取消,并抛出一个 OperationCanceledException。

 

任务完成时自动启动新任务

伸缩性好的软件不该该使线程阻塞。调用Wait ,或者在任务尚未完成时查询任务的Result 属性。这样操做可能会形成线程池建立新线程,这增大了资源的消耗,也不利于性能和伸缩性。幸亏还有更好的办法知道线程何时结束,还能在结束时启动新的 Task。

// Create and start a Task, continue with another task
Task<Int32> t = Task.Run(() => Sum(CancellationToken.None, 10000));
// ContinueWith returns a Task but you usually don't care
Task cwt = t.ContinueWith(task => Console.WriteLine("The sum is: " + task.Result));

ContinueWith 方法,它返回的是对新 Task 对象的引用。能够用这个对象调用各类成员。还有 Task 对象内部包含了ContinueWith 任务的一个集合。因此可用一个Task 对象来屡次调用COntinueWith。任务完成时,全部ContinueWith 任务都会进入线程池的队列中。

TaskContinueationOptions 枚举值 可在调用ContinueWith 时传进去。

[Flags, Serializable]
public enum TaskContinuationOptions {
   None = 0x0000,// The default
   // Hints to the TaskScheduler that you want this task to run sooner than later.
   PreferFairness = 0x0001,
   // Hints to the TaskScheduler that it should more aggressively create thread pool threads.
   LongRunning = 0x0002,
   // Always honored: Associates a Task with its parent Task (discussed shortly)
   AttachedToParent = 0x0004,
   // If a task attempts to attach to this parent task, an InvalidOperationException is thrown.
   DenyChildAttach = 0x0008,
   // Forces child tasks to use the default scheduler as opposed to the parent’s scheduler.
   HideScheduler = 0x0010,
   // Prevents completion of the continuation until the antecedent has completed.
   LazyCancellation = 0x0020,
   // This flag indicates that you want the thread that executed the first task to also
   // execute the ContinueWith task. If the first task has already completed, then the
   // thread calling ContinueWith will execute the ContinueWith task.
   ExecuteSynchronously = 0x80000,
   // These flags indicate under what circumstances to run the ContinueWith task
   NotOnRanToCompletion = 0x10000,
   NotOnFaulted = 0x20000,
   NotOnCanceled = 0x40000,
   // These flags are convenient combinations of the above three flags
   OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
   OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
   OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
}

调用 ContinueWith 时,可用 TaskContinuationOptions.OnlyOnCanceled 标志指定新任务只有在第一个任务被取消时才执行。相似地 OnlyOnFaulted 只有在第一个任务抛出未处理的异常时才执行。OnlyOnRanToCompletion 只有在第一个任务顺利执行完成时才执行。

默认状况下,若是不指定上述任何标志,则新任务不管如何都会运行,无论第一任务如何完成。

 

任务能够启动子任务

任务支持父/子关系,以下代码所示:

Task<Int32[]> parent = new Task<Int32[]>(() => {
   var results = new Int32[3]; // Create an array for the results
   // This tasks creates and starts 3 child tasks
   new Task(() => results[0] = Sum(10000), TaskCreationOptions.AttachedToParent).Start();
   new Task(() => results[1] = Sum(20000), TaskCreationOptions.AttachedToParent).Start();
   new Task(() => results[2] = Sum(30000), TaskCreationOptions.AttachedToParent).Start();
   // Returns a reference to the array (even though the elements may not be initialized yet)
   return results;
});
// When the parent and its children have run to completion, display the results
var cwt = parent.ContinueWith(
parentTask => Array.ForEach(parentTask.Result, Console.WriteLine));
// Start the parent Task so it can start its children
parent.Start();

一个任务建立的一个或多个 Task 对象默认是顶级任务,他们与建立它们的任务无关。但 TaskCreationOptions.AttachedToParent 标志将一个Task 和建立它的 Task关联,结果是除非全部子任务(以及子任务的子任务)结束运行,不然建立(父任务)不认为已经结束。

 

Task内部揭秘

每一个Task 对象都有一组字段,这些字段构成了任务的状态。其中包括 :

  • 一个Int32 ID;(表明Task 惟一ID 的 Int32 字段。从 1 开始,每分配一个ID都递增1。系统分配的表明 Task 执行状态的一个 Int32 )

  • 对父任务的引用、

  • 对Task建立时指定的 TaskScheduler 的引用、

  • 对回调方法的引用、

  • 对要传给回调方法对象的引用、

  • 对 ExecutionContext 的引用以及对 ManualResetEventSlim 对象的引用。

另外,每一个 Task 对象都有对根据须要建立的补充状态的引用。补充状态包含:

  • 一个CancellationToken、

  • 一个 ContinueWithTask 对象集合、

  • 为抛出未处理异常的子任务而准备的一个Task 对象集合等。

Task 很好用,但也是有代价的。必须为全部这些状态分配内存。若是不须要任务的附加功能,那么使用 ThreadPool.QueueUserWorkItem 能得到更好的资源利用率。

Task 和 Task<TResult> 类实现了 IDisposable 接口。现在 ,全部Dispose 方法所作的都是关闭 ManualResetEventSlim 对象。 但能够从 Task 和 Task<TResult> 派生的类,在这些类中分配它们本身的资源,并在它们重写的 Dispose 方法中释放这些资源。但不建议为Task对象显示调用 Dispose。

在一个 Task 对象的存在期间,可查询 Task 的只读 Status 属性了解 它在其生存期的什么位置。该属性返回一个 TaskStatus 值。

public enum TaskStatus {
   // These flags indicate the state of a Task during its lifetime:
   Created, // Task created explicitly; you can manually Start() this task
   WaitingForActivation,// Task created implicitly; it starts automatically
   WaitingToRun, // The task was scheduled but isn’t running yet
   Running, // The task is actually running
   // The task is waiting for children to complete before it considers itself complete
   WaitingForChildrenToComplete,
   // A task's final state is one of these:
   RanToCompletion,
   Canceled,
   Faulted
}

当任务完成时,状态变成 如下状态之一:RanToCompletion、Canceled 或 Faulted。若是任务完成,可经过Task<TResult> 的Result 属性来查询任务结果。Task 或 Task<TResult>出错时,可查询 Task 的 Exception 属性获取异常,该属性老是返回一个AggregateException 对象,对象的 InnerExceptions 集合包含了全部未处理的异常。

为了简化代码,Task 提供了几个只读 Boolean 属性,包括IsCanceled 、 IsFaulted和 Iscompleted。注意当 Task 处于 RanToCompletion ,Canceled 或 Faulted 状态时,IsCompleted 返回 true。判断一个 Task 是否成功完成 最简单的办法是使用以下代码:

if(task.Status == TaskStatus.RanToCompletion) ...

 

任务工厂 - TaskFactory

有时须要建立一组共享相同配置的 Task 对象。为避免机械地将相同的参数传给每一个Task 的构造器,能够建立一个任务工厂来封装通用的配置。System.Threading.Tasks 命名空间定义了一个 TaskFactory 类型和一个 TaskFactory<TResult> 类型。

如下代码延时了如何使用一个TaskFactory:

Task parent = new Task(() => {
   var cts = new CancellationTokenSource();
   var tf = new TaskFactory<Int32>(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
   // This task creates and starts 3 child tasks
   var childTasks = new[] {
       tf.StartNew(() => Sum(cts.Token, 10000)),
       tf.StartNew(() => Sum(cts.Token, 20000)),
       tf.StartNew(() => Sum(cts.Token, Int32.MaxValue)) // Too big, throws OverflowException
  };
   // If any of the child tasks throw, cancel the rest of them
   for (Int32 task = 0; task < childTasks.Length; task++)
  childTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
   // When all children are done, get the maximum value returned from the
   // non-faulting/canceled tasks. Then pass the maximum value to another
   // task that displays the maximum result
   tf.ContinueWhenAll(
       childTasks,
       completedTasks =>
       completedTasks.Where(t => t.Status == TaskStatus.RanToCompletion).Max(t => t.Result),
      CancellationToken.None).ContinueWith(t=>Console.WriteLine("The maximum is: " + t.Result),
  TaskContinuationOptions.ExecuteSynchronously);
});

// When the children are done, show any unhandled exceptions too
parent.ContinueWith(p => {
   // I put all this text in a StringBuilder and call Console.WriteLine just once
   // because this task could execute concurrently with the task above & I don't
   // want the tasks' output interspersed
   StringBuilder sb = new StringBuilder(
   "The following exception(s) occurred:" + Environment.NewLine);
   foreach (var e in p.Exception.Flatten().InnerExceptions)
  sb.AppendLine(" "+ e.GetType().ToString());
   Console.WriteLine(sb.ToString());
}, TaskContinuationOptions.OnlyOnFaulted);
// Start the parent Task so it can start its children
parent.Start();

 

任务调度器

任务基础结构很是灵活,其中TaskScheduler 对象功不可没。TaskScheduler 对象负责执行被调度的任务。FCL 提供了 两个派生自TaskScheduler 的类型:

  • 线程池任务调度器 ( thread pool task scheduler)

  • 同步上下文任务调度器 (synchronization context task scheduler)

默认状况下是线程池任务调度器,这个任务调度器将任务调度给线程池的工做者线程。

同步上下文任务调度器适合提供了图形用户界面的应用程序,例如:windows 窗体、windows Presentation Foundation(WPF)、Silverlight、Windows Store 应用程序。它将全部任务都调度给应用程序的GUI 线程,使全部任务代码都能成功更新UI 组件。该任务调度器也不适用线程池。

下面的代码演示如何使用 同步上下文任务调度器:

internal sealed class MyForm : Form {
   private readonly TaskScheduler m_syncContextTaskScheduler;
   public MyForm() {
       // Get a reference to a synchronization context task scheduler
       m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
       Text = "Synchronization Context Task Scheduler Demo";
       Visible = true; Width = 600; Height = 100;
  }
   
   private CancellationTokenSource m_cts;
   
   protected override void OnMouseClick(MouseEventArgs e) {
       if (m_cts != null) { // An operation is in flight, cancel it
           m_cts.Cancel();
           m_cts = null;
      } else { // An operation is not in flight, start it
           Text = "Operation running";
           m_cts = new CancellationTokenSource();
           // 这个任务使用默认的任务调度器,在一个线程池线程上执行
           Task<Int32> t = Task.Run(() => Sum(m_cts.Token, 20000), m_cts.Token);
           // 这些任务使用同步上下文任务调度器,在GUI 线程中执行
           t.ContinueWith(task => Text = "Result: " + task.Result,
           CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
           m_syncContextTaskScheduler);
           t.ContinueWith(task => Text = "Operation canceled",
           CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled,
           m_syncContextTaskScheduler);
           t.ContinueWith(task => Text = "Operation faulted",
           CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
           m_syncContextTaskScheduler);
      }
  base.OnMouseClick(e);
  }
}

 

若是有特殊的任务需求,彻底能够定义本身的TaskScheduler 派生类。

 

Parallel 的静态 For,ForEach,和 Invoke 方法

一些常见的编程情形可经过任务提高性能。为简化编程,静态 System.Threading.Tasks.Parallel 类封装了这些情形,它内部使用Task 对象。

不要像下面这样处理集合中的全部项,例如:

//一个线程顺序执行这个工做(每次迭代调用一次 Dowork)
for (Int32 i=0; i< 1000; i++) DoWork(i);

使用Parallel 类的For方法,用多个线程池线程辅助完成工做:

//线程池的线程并行处理工做
Parallel.For(0, 1000, i=>DoWork(i));

若是能用foreach 的地方能够这样写:

//线程池的线程并行处理工做
Parallel.ForEach(collection , item => DoWork(item));

能用For的地方尽可能用for,由于它更快。

若是要执行多个方法,能够像下面这样执行:

Parallel.Invoke(
()=>method1();
()=>method2();
()=>method3();)

若是只为区区几个工做项使用Parallel的方法,或者为处理得很是快的工做项使用Parallel 的方法,就会得不偿失,反而下降性能。由于Parallel 的方法自己也有开销。

 

定时器的使用

System.Threading 命名空间定义了一个 Timer 类,可用它让一个线程池线程定时调用一个方法。

在内部,线程池为全部 Timer 对象只使用一个线程。这个线程知道下一个Timer 对象在何时到期(计时器还有多久触发)。下一个Timer 对象到期时,线程就会唤醒,在内部调用 ThreadPool 的 QueueUserWorkItem,将一个工做项添加到线程池的队列中,使你的回调方法获得调用。

若是回调方法的执行时间很长,计时器可能在上个回调还没完成的时候再次触发,这可能形成多个线程池线程同时执行你的回调方法。为解决这个问题,构造Timer 时,为period 参数指定 Timeout.Infinite。这样,计时器就只触发一次,而后,在你的回调方法中,调用Change 方法来指定一个新的dueTime,并再次为period 参数指定Timeout.Infinite。

Timer 类提供了一个 Dispose 方法,容许彻底取消计时器,并可在当时出于pending 状态的全部回调完成以后,向notifyObject 参数标识的内核对象发出信号。

如下代码演示了如何让一个线程池线程当即回调方法,之后每2 秒调用一次:

internal static class TimerDemo {
   private static Timer s_timer;
   public static void Main() {
       Console.WriteLine("Checking status every 2 seconds");
       // Create the Timer ensuring that it never fires. This ensures that
       // s_timer refers to it BEFORE Status is invoked by a thread pool thread
       s_timer = new Timer(Status, null, Timeout.Infinite, Timeout.Infinite);
       // Now that s_timer is assigned to, we can let the timer fire knowing
       // that calling Change in Status will not throw a NullReferenceException
       s_timer.Change(0, Timeout.Infinite);
       Console.ReadLine(); // Prevent the process from terminating
  }
   // This method's signature must match the TimerCallback delegate
   private static void Status(Object state) {
       // This method is executed by a thread pool thread
       Console.WriteLine("In Status at {0}", DateTime.Now);
       Thread.Sleep(1000); // Simulates other work (1 second)
       // Just before returning, have the Timer fire again in 2 seconds
       s_timer.Change(2000, Timeout.Infinite);
       // When this method returns, the thread goes back
       // to the pool and waits for another work item
  }
}

若是有须要定时执行的操做,可利用Task 的静态Delay 方法和 C# 的async 和 await 关键字来编码。

internal static class DelayDemo {
   public static void Main() {
       Console.WriteLine("Checking status every 2 seconds");
       Status();
       Console.ReadLine(); // Prevent the process from terminating
  }
   // This method can take whatever parameters you desire
   private static async void Status() {
       while (true) {
           Console.WriteLine("Checking status at {0}", DateTime.Now);
           // Put code to check status here...
           // At end of loop, delay 2 seconds without blocking a thread
           await Task.Delay(2000); // await allows thread to return
           // After 2 seconds, some thread will continue after await to loop around
      }
  }
}

 

FCL 只提供几个计时器,下面介绍这几个计时器的特色:

  • System.Threading 的 Timer 类

    要在一个线程池线程上执行定时的后台任务,最好用它。

  • System.WIndows.Forms 的 Timer 类

    构造这个类的实例,至关于告诉WIndows 将一个计时器和调用线程关联。当这个计时器触发时,Windows 将一条计时器消息(WM_TIMER) 注入线程的消息队列。线程必须执行一个消息泵来提取这些消息,并把它们派发给须要的回调方法。注意,全部这些工做都只由一个线程完成 (设置计时器的线程保证就是执行回调方法的线程)。还意味着计时器方法不会由多个线程并发执行。

  • System.Windows.Threading 的 DispatcherTimer 类

    这个类是 System.Windows.Forms 的 Timer 类在 Silverlight 和 WPF 应用程序中的等价物。

  • Windows.UI.Xaml 的 DispatcherTimer 类

    这个类是System.Windows.Forms’s Timer 类 在Windows Store 应用中的等价物。

  • System.Timers’s Timer 类

    它本质上是 System.Threading 的 Timer 类的包装类。

 

线程池如何管理线程

CLR 团队将线程池默认 大约 1000 个线程。这基本上是不设限制。由于一个32位 进程最大有 2 GB 的可用空间。加载了一组 Win32 和CLR Dlls,并分配了本地堆和托管堆以后,剩余约 1.5 GB 的地址空间。因为每一个线程都要为其用户模式栈 和 线程环境变量块(TEB)主备超过1 MB 的内存,因此在一个 32 位进程中,最多能有大约 1360 线程。

System.Threading.ThreadPool 类提供了几个静态方法,可调用它们设置和查询线程池线程的线程数:

GetMaxThreads、SetMaxThreads、GetMinThreads、SetMinThreads 和 GetAvailableThreads

Jeffrey Richter 强烈建议不要调用上述任何方法。Jeffrey 说设置线程池的线程数量会让程序的性能变得更差。我我的以为线程池的数量不是应该保持在和CPU 数量的2倍上吗?

 

如何管理工做者线程

ThreadPool.QueueUserWorkItem 方法和 Timer 类老是将工做项放到全局队列中。工做者线程采用一个先入先出FIFO 算法将工做项从这个队列中取出,并处理它们。

因为是多个工做者线程在全局队列中拿走工做项,这就会造成并发情形,要有一个线程同步锁,保证两个或多个线程不会获取同一个工做项。这个线程同步锁在某些应用程序中可能成为瓶颈。

每一个工做者线程都有本身的本地队列,工做者线程调度一个Task 时,该Task 被添加到调用线程的本地队列. 工做者线程采用先入后出 (LIFO)算法将任务从本地队列取出.

 

 

 

 工做者线程发现它的本地线程队列变空了 , 会尝试从另外一个工做者线程的本地队列"偷" 一个Task , 这个Task 从本地队列的尾部 "偷走" , 并要求获取一个线程同步锁 .

若是全部本地队列都变空 , 工做者线程会从全局队列中取一个工做项 .

若是全局队列也为空 , 工做者线程会进入睡眠状态 , 等待事情的发生 .

若是睡眠时间太长了, 他会本身醒来, 销毁自身, 容许系统回收线程使用的资源( 内核对象, 栈, TEB 等)。

相关文章
相关标签/搜索