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 等)。

相關文章
相關標籤/搜索