zoukankan      html  css  js  c++  java
  • CLR 线程池

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

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

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

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

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

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

    • 如果线程池中没有就创建一个,线程执行完后,再收回到线程池中。

    • 如果应用程序向线程池发了很多请求,一个线程忙不过来就会再创建更多的线程。

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

     

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

     

     

  • 相关阅读:
    Gym 100553B Burrito King 无脑背包
    BestCoder Round #85 A B C
    poj 1687 Buggy Sat 简单计算几何
    HDU 1863 Kruskal求最小生成树
    记2016商大ACM省赛
    COMP9517 Week7 Tracking
    COMP9517 week7 Motion
    COMP9313 week7b Spark SQL
    COMP9313 Week 7 Product Quantization and K-Means Clustering
    COMP9517 lab3 image segementation
  • 原文地址:https://www.cnblogs.com/mingjie-c/p/11734608.html
Copyright © 2011-2022 走看看