zoukankan      html  css  js  c++  java
  • 《CLR via C#》之线程处理——线程池与任务

    《CLR via C#》之线程处理——线程池与任务

    线程池

    每一个CLR都有一个线程池——由这个CLR控制的所有AppDomain共享。如果一个进程加载了多个CLR,那么每个CLR都有它自己的线程池。
    CLR初始化化时,线程池是空的。在内部线程池维护了一个操作请求队列。应用请求异步操作时,就将一个记录项添加到队列中。线程池从队列中提取这个记录项,将这个记录项Dispatch给一个线程池线程。如果线程池没有线程,就创建一个新线程。当线程执行完后,也不会被销毁。
    如果线程池闲着没事干一段时间之后,线程会终止自己以释放资源。

    线程池用法

    通常,调用ThreadPool类的静态方法:

    1. static Boolean QueueUserWorkItem(WaitCallback callBack);//callBack: 回调委托
    2. static Boolean QueueUserWorkItem(WaitCallback callBa ck, Object state);//state:回调参数

    任务

    使用ThreadPool.QueueUserWorkItem方法有两个限制:

    1. 没有内建机制通知操作何时完成;
    2. 没有机制在操作完成时获得返回值。

    因此,推出了Task来完成相同的事

    1. ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5);//调用QueueUserWorkItem
    2. new Task(ComputeBoundOp, 5).Start(); //用Task做相同的事
    3. Task.Run(() => ComputeBoundOp(5)); //等价写法

    在构造Task时,可以传递TaskCreationOptions标识来控制Task的执行方式。

    1. [Flags, Serializable]
    2. public enum TaskCreationOptions
    3. {
    4. None = 0x0000, //默认
    5. // 提议TaskScheduler,希望尽快执行
    6. PreferFairness = 0x0001,
    7. // 提议TaskScheduler,希望尽可能创建线程池线程
    8. LongRunning = 0x0002,
    9. // 总被采纳:将Task和它的父Task关联
    10. AttachedToParent = 0x0004,
    11. // 总被采纳:如果一个Task试图和父Task连接,它就是个普通Task,而不是子Task
    12. DenyChidAttach = 0x0008,
    13. // 总被采纳:强迫子Task使用默认调度器而不是父Task的调度器
    14. HideScheduler = 0x0010
    15. }

    等待任务完成并获取结果(Wait方法和Result属性)

    1. Task<int> t = new Task<int>(n => Sum((int)n), 100000000, TaskCreationOptions.LongRunning);
    2. t.Start();
    3. // 可选择显式等待任务完成
    4. t.Wait();
    5. // 可获得结果(Result属性内部会调用Wait)
    6. Console.WriteLine("The Sum is " + t.Result);

    注意:Result属性内部会调用Wait方法。


    取消任务(token的ThrowIfCancellationRequested方法)

    Task和ThreadPool取消任务的区别是,前者在操作中调用用ThrowIfCancellationRequested抛出异常,后者检查IsCancellationRequested属性。原因是Task可以返回值,需要将已完成的任务和出错的任务区分开。

    1. internal static class TaskDemo
    2. {
    3. private static Int32 Sum(CancellationToken token, int n)
    4. {
    5. int sum = 0;
    6. for (; n > 0; n--)
    7. {
    8. // 如果token已取消,调用该方法会抛出一个OperationCanceledException
    9. token.ThrowIfCancellationRequested();
    10. checked
    11. {
    12. sum += 10;
    13. }
    14. }
    15. return sum;
    16. }
    17. public static void Go()
    18. {
    19. CancellationTokenSource cts = new CancellationTokenSource();
    20. // 创建Task,并立即Start
    21. Task<int> t = Task.Run(() => Sum(cts.Token, 100000000), cts.Token);
    22. // 在之后的某个时间,取消task
    23. cts.Cancel();
    24. try
    25. {
    26. //如果任务已经取消,Result会抛出AggregateException
    27. Console.WriteLine("The sum is :" + t.Result);
    28. }
    29. catch(AggregateException x)
    30. {
    31. // 将任何OperationCancelException都视为已处理。
    32. // 其它任何异常都造成抛出一个新的AggregateException,其中只包含未处理的异常。
    33. x.Handle(e => e is OperationCanceledException);
    34. Console.WriteLine("Sum was cancelled");
    35. }
    36. }
    37. }

    注意:如果试图取消一个未Start的任务,会抛出InvalidOperationException。


    任务完成自动启动新任务(ContinueWith方法)

    1. public static void TaskContinueWith()
    2. {
    3. Task<int> t = Task.Run(() => Sum(10000));
    4. t.ContinueWith( task => Console.WriteLine("The sum is: " + task.Result),
    5. TaskContinuationOptions.OnlyOnRanToCompletion);
    6. t.ContinueWith(task => Console.WriteLine("Sum threw: " + task.Exception.InnerException),
    7. TaskContinuationOptions.OnlyOnFaulted);
    8. //ContinueWith返回Task,但一般都不需要保存它
    9. Task cwt = t.ContinueWith(task => Console.WriteLine("Sum was cancelled"),
    10. TaskContinuationOptions.OnlyOnCanceled);
    11. }

    TaskContinuationOptions枚举类型的前六个标识与TaskCreationOptions一致:

    1. [Flags, Serializable]
    2. public enum TaskContinuationOptions
    3. {
    4. None = 0x0000,// 默认
    5. // 提议TaskScheduler,希望任务尽快执行
    6. PreferFairness = 0x0001,
    7. // 提议TaskScheduler:应尽可能创建线程池线程
    8. LongRunning = 0x0002,
    9. // 总被采纳:将一个Task与它的父Task关联
    10. AttachedToParent = 0x0004,
    11. // 任务试图和这个父任务连接将抛出一个InvalidOperationException
    12. DenyChildAttach = 0x0008,
    13. // 强迫子任务使用默认调度器而不是父任务的调度器
    14. HideScheduler = 0x0010,
    15. // 除非前置任务完成,否则禁止延续任务完成(取消)
    16. LazyCancellation = 0x0020,
    17. // 这个标识指出你希望由执行第一个任务的线程执行ContinueWith任务。
    18. // 第一个任务完成后,调用ContinueWith的线程接着执行ContinueWith任务。
    19. ExecuteSynchronously = 0x80000,
    20. // 这些标识指出在什么情况下运行ContinueWith任务
    21. NotOnRanToCompletion = 0x10000,
    22. NotOnFaulted = 0x20000,
    23. NotOnCanceled = 0x40000,
    24. // 这些标识是以上三个标识的便利组合
    25. OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
    26. OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
    27. OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
    28. }

    注意:ExecuteSynchronously:同步执行,两个任务在同一个线程一前一后地执行,被称为同步执行。


    任务启动子任务

    1. public static void TaskParentDemo()
    2. {
    3. Task<Int32[]> parent = new Task<Int32[]>(() =>
    4. {
    5. var results = new Int32[3]; //创建一个数组来存储结果
    6. //这个任务创建并启动3个子任务
    7. new Task(() => results[0] = Sum(10000), TaskCreationOptions.AttachedToParent).Start();
    8. new Task(() => results[1] = Sum(20000), TaskCreationOptions.AttachedToParent).Start();
    9. new Task(() => results[2] = Sum(30000), TaskCreationOptions.AttachedToParent).Start();
    10. return results;
    11. });
    12. // 父任务及其子任务运行完成后,用一个延续任务显示结果
    13. var cwt = parent.ContinueWith(
    14. parentTask => Array.ForEach(parentTask.Result, Console.WriteLine));
    15. parent.Start();
    16. }

    注意:一个Task创建的Task默认为顶级任务,与创建它的Task无关。但是使用TaskCreationOptions.AttachedToParent标志关联Task和创建它的Task,除非所有子Task结束,否则父Task不认为已经结束。


    任务工厂(TaskFactory和TaskFactory

    有时需要创建一组共享相同配置的Task对象,可创建一个任务工厂来封装通用的配置。System.Threading.Tasks命名空间提供了TaskFactory和TaskFactory
    构造一个任务工厂,向它传递希望任务具有的CancellationToken,TaskScheduler,TaskCreationOptions和TaskContinuationOptions设置。

    1. public static void TaskFactoryDemo()
    2. {
    3. Task parent = new Task(() =>
    4. {
    5. var cts = new CancellationTokenSource();
    6. var tf = new TaskFactory<Int32>(
    7. cts.Token,
    8. TaskCreationOptions.AttachedToParent,
    9. TaskContinuationOptions.ExecuteSynchronously,
    10. TaskScheduler.Default);
    11. var childTasks = new[]
    12. {
    13. tf.StartNew(() => Sum(cts.Token, 10000)),
    14. tf.StartNew(() => Sum(cts.Token, 20000)),
    15. tf.StartNew(() => Sum(cts.Token, Int32.MaxValue))
    16. };
    17. // 任何子任务抛出异常,就取消其余子任务
    18. for (int task = 0; task < childTasks.Length; task++)
    19. {
    20. childTasks[task].ContinueWith(
    21. t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
    22. }
    23. // 所有子任务完成后,从未出错/未取消的任务获取返回的最大值
    24. // 然后将最大值传给另一个任务来显示最大结果。
    25. // **注意**:由于这个任务由任务工厂创建,所以会任务是父任务的一个子任务(默认配置)
    26. // 显示CancellationToken.None,和TaskContinuationOptions.ExecuteSynchronously覆盖默认值
    27. tf.ContinueWhenAll(
    28. childTasks,
    29. completedTasks => completedTasks.Where(
    30. t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result),
    31. CancellationToken.None)
    32. .ContinueWith(t => Console.WriteLine("The maxium is:" + t.Result),
    33. TaskContinuationOptions.ExecuteSynchronously);
    34. });
    35. // 子任务完成后,显示任何未处理的异常
    36. parent.ContinueWith(p =>
    37. {
    38. StringBuilder sb = new StringBuilder(
    39. "The following exception(s) occurred:" + Environment.NewLine);
    40. foreach (var e in p.Exception.Flatten().InnerExceptions)
    41. {
    42. sb.AppendLine(" " + e.GetType().ToString());
    43. }
    44. Console.WriteLine(sb.ToString());
    45. }, TaskContinuationOptions.OnlyOnFaulted);
    46. parent.Start();
    47. }

    注意:任务工厂的ContinueWhenAll和ContinueWhenAny时,NotOnRanToCompletion,NotOnFaulted和NotOnCanceled标识是非法的。组合标识当然也是非法的(OnlyOnCanceled,OnlyOnFaulted 和OnlyOnRanToCompletion)也是非法的。即,无论前置任务是如何完成的,ContinueWhenAll和ContinueWhenAny都会执行延续任务。





  • 相关阅读:
    ASP.NET Interview Questions
    [上海]招聘.Net高级软件工程师&BI数据仓库工程师(急)
    访问被拒绝:“microsoft.web.ui.webcontrols”的解决办法
    上海市区广场、商场、大厦中英文对照大全
    文件夹加密码方法
    JavaScript:使用面向对象的技术创建高级 Web 应用程序(转)
    对数据类型而言运算符无效。运算符为 add,类型为 text。
    SQL行列转换
    主成分分析(Principal components analysis)最小平方误差解释
    主成分分析(Principal components analysis)最大方差解释
  • 原文地址:https://www.cnblogs.com/qianzi067/p/5818728.html
Copyright © 2011-2022 走看看