好久没有更新了,今天来一篇,算是《同步与异步》系列的开篇吧,加油,坚持下去(PS:越来越懒了)。

一、Thread

利用Thread 可以直接创建和控制线程,在我的认知里它是最古老的技术了。因为out了、所以不再写例子了。

二、ThreadPool

由于线程的创建和销毁需要耗费大量的资源,为了提过性能、引入了线程池、即ThreadPool,ThreadPool 可隐式完成线程的创建和分配管理工作。

以下是来自MSDN的几句备注:

  线程池根据需要提供新的工作线程或 I/O 完成线程,直到其达到每个类别的最小值。 当达到最小值时,线程池可以在该类别中创建更多线程或等待某些任务完成。 从 .NET Framework 4 开始,线程池会创建和销毁工作线程以优化吞吐量,吞吐量定义为单位时间内完成的任务数。 线程过少时可能无法更好地利用可用资源,但线程过多时又可能会加剧资源的争用情况。

直接应用 ThreadPool时、有两种应用场景:

1、将需要异步执行的方法、排入队列,当有可用线程时执行被排入队列的方法

  1. // 该应用场景下 ThreadPool 提供如下两种形式的重载方法
  2. // public static bool QueueUserWorkItem(WaitCallback callBack);
  3. // public static bool QueueUserWorkItem(WaitCallback callBack, object state);
  4.  
  5. // WaitCallback 为 delegate, 一个object类型的入参,没有返回值。
  6. // public delegate void WaitCallback(object state);
  7.  
  8. base.SetTip(nameof(ThreadPool.QueueUserWorkItem));
  9.  
  10. ThreadPool.QueueUserWorkItem((state) =>
  11. {
  12. this.SetTip("等待一秒");
  13. Thread.Sleep();
  14. this.SetTip("任务执行完毕");
  15. });

2、注册等待信号对象(WaitHandle)、并在其收到信号或超时时触发回调函数

  1. // 该应用场景下 ThreadPool 提供了四种形式的重载方法, 下面的重载形式在我看来是最具有直观意义的
  2. // public static RegisteredWaitHandle RegisterWaitForSingleObject(
  3. // WaitHandle waitObject, WaitOrTimerCallback callBack, object state, long millisecondsTimeOutInterval, bool executeOnlyOnce);
  4.  
  5. // 其中 WaitHandle 为需要等待信号的类型 的 抽象基类,在后续随笔中会做详细介绍,在此不再多言。
  6.  
  7. // 其中 WaitOrTimerCallback 为 回调函数委托
  8. // public delegate void WaitOrTimerCallback(object state, bool timedOut);
  9.  
  10. // state 参数为 回调函数的传入参数
  11.  
  12. // millisecondsTimeOutInterval 参数为 计时器超时周期, -1 为永不超时、即一直等待。
  13. // 对于此参数、第一次接触到的人可能有疑问、怎么还有周期?
  14. // 因为 信号可以重复接到多次、所以当每次接到信号后、或者超时后计时器都会重新计时, 所以有了周期的含义。
  15.  
  16. // executeOnlyOnce, True 表示只执行一次, False 会一直等到该信号对象被取消注册,否则 只要接到信号或者超时就会触发回调函数。
  17.  
  18. base.SetTip(nameof(ThreadPool.RegisterWaitForSingleObject));
  19.  
  20. ThreadPool.RegisterWaitForSingleObject(this.waitObject, (state, timeout) =>
  21. {
  22. this.SetTip("++++++等待对象收到信号++++++");
  23.  
  24. }, null, -, false);
  25.  
  26. ThreadPool.QueueUserWorkItem((state) =>
  27. {
  28. this.SetTip("等待一秒");
  29. Thread.Sleep();
  30.  
  31. this.SetTip("等待对象发出信号");
  32. this.waitObject.Set();
  33.  
  34. this.SetTip("等待5秒");
  35. Thread.Sleep();
  36.  
  37. this.SetTip("等待对象发出信号");
  38. this.waitObject.Set();
  39.  
  40. });

三、Delegate.BeginInvoke

Delegate.BeginInvoke 间接的调用了线程池、从线程池中获取一个可用线程、执行当前委托所指向的函数指针所代表的方法。

  1. [Tag("Delegate.BeginInvoke")]
  2. private void Demo3()
  3. {
  4. this.txtTip.SetTip("UI, Id:" + Thread.CurrentThread.ManagedThreadId);
  5.  
  6. Action action = new Action(this.DelegateTest);
  7. action.BeginInvoke(null, null);
  8. action.BeginInvoke(null, null);
  9.  
  10. }
  11.  
  12. private void DelegateTest()
  13. {
  14. int id = Thread.CurrentThread.ManagedThreadId;
  15.  
  16. this.Dispatcher.Invoke(() =>
  17. {
  18. this.txtTip.SetTip("BeginInvoke, Id:" + id);
  19. });
  20. }

通过 Thread.CurrentThread.ManagedThreadId 、我们也可以间接的证明 BeginInvoke 确实时在不同的线程中执行的。

当然,与之对应的是一系列的 Begin...  End...  方法对, 它们都是 IAsyncResult系列的异步编程模型中的一份子。

在 IAsyncResult系列的异步编程模型中,传递参数 和 获取返回值的Demo 见下:

  1. [Tag("Delegate.BeginInvoke带有参数和返回值")]
  2. private void Demo4()
  3. {
  4. Func<string, string> func = new Func<string, string>(this.DelegateTest2);
  5.  
  6. this.txtTip.SetTip(" 输入参数 123 ");
  7.  
  8. func.BeginInvoke(" 123 ", new AsyncCallback((System.IAsyncResult result) =>
  9. {
  10. Func<string, string> tmp = result.AsyncState as Func<string, string>;
  11. if (tmp != null)
  12. {
  13. string returnResult = tmp.EndInvoke(result);
  14.  
  15. this.Dispatcher.Invoke(() =>
  16. {
  17. this.txtTip.SetTip(" 函数回调 结果 : " + returnResult);
  18. });
  19. }
  20. }), func);
  21.  
  22. }
  23. private string DelegateTest2(string args)
  24. {
  25. return args + " : Return args";
  26. }

四、Task

Task是在 .NET 4.0 中新增的,是基于任务的异步模型。它是对线程池的再次封装、使得可以更加方便的创建多种任务组合,如 顺序性的延续任务、父子任务。也可以方便的粗粒度地控制任务优先级。

1)Task.NET 4.0 中提倡的方式

Task 对外公开了构造函数、但是微软并不建议直接使用Task构造函数去实例化对象,而是 使用 Task.Factory.StartNew();

MSDN 中的备注如下:
For performance reasons, TaskFactory's StartNew method should be the preferred mechanism for creating and scheduling computational tasks,
but for scenarios where creation and scheduling must be separated, the constructors may be used,
and the task's Start method may then be used to schedule the task for execution at a later time.
Task 类还提供了初始化任务但不计划执行任务的构造函数。 出于性能方面的考虑,TaskFactory 的 StartNew 方法应该是创建和计划计算任务的首选机制,
但是对于创建和计划必须分开的情况,可以使用构造函数,然后可以使用任务的 Start 方法计划任务在稍后执行。

  1. Task.Factory.StartNew(() =>
  2. {
  3. base.SetTip("Task.Factory.StartNew(一个参数)");
  4. }).ContinueWith((t) =>
  5. {
  6. base.SetTip(t.Id.ToString());
  7. base.SetTip(t.CreationOptions.ToString());
  8. }).ContinueWith((t) =>
  9. {
  10. base.SetTip(t.Id.ToString());
  11. base.SetTip(t.CreationOptions.ToString());
  12. });

Task.Factory.StartNew 提供了高达16个的重载函数。
其中 Task.Factory.StartNew<TTask> 是创建带有返回值的异步任务。

以最复杂的重载为例、逐一介绍其参数
public Task<TResult> StartNew<TResult>(Func<object, TResult> function, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler);

function : 回调函数、我想没有必要做解释吧。
state : 回调函数的传入参数
CancellationToken: 用以取消Task (后续随笔会做详细介绍)
TaskCreationOptions: 指定可控制任务的创建和执行的可选行为的标志(后续随笔会做详细介绍)
TaskScheduler: 一个实例 TaskScheduler 类表示一个任务计划程序. 该值一般都是使用 TaskScheduler.Default

也就是说:
Task.Factory.StartNew(()=> { });

Task.Factory.StartNew(()=> { }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
两种方式效果是一致的。

如果你想更精细化的控制任务、可以使用其他重载方法、传递不同参数以达到预想的目的。

2)Task初始化任务但并不计划执行

前文说过 Task 提供了构造函数、它提供了初始化任务,但并不去计划执行的方式。
让我们再看一下 Task 得构造函数吧,还是以最复杂的为例:
public Task(Func<object, TResult> function, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
其参数和 Task.Factory.StartNew 相比、 少了TaskScheduler。在性能方面MSDN提示后者会更好。

  1. base.SetTip("初始化任务");
  2. Task task = new Task(() =>
  3. {
  4. base.SetTip("被计划的任务开始执行");
  5.  
  6. base.SetTip("任务休眠5秒");
  7. Thread.Sleep();
  8.  
  9. base.SetTip("任务执行完毕");
  10. });
  11.  
  12. // 为了保证能实时更新UI、看到代码执行效果、故而将代码异步执行
  13. Task.Factory.StartNew(() =>
  14. {
  15. base.SetTip("休眠两秒");
  16. Thread.Sleep();
  17.  
  18. base.SetTip("将任务列入执行计划");
  19. task.Start();
  20.  
  21. base.SetTip("等待Task执行完毕");
  22. task.Wait();// Wait方法 会等待Task执行完毕
  23. base.SetTip("Task执行完毕");
  24. });

另外再强调一点:Task.Start(), 只是将任务列入执行计划,至于任务什么时候去执行则取决于线程池中什么时候有可用线程。
Task.Factory.StartNew 也是一样的。

好了,Task类的介绍、到此为止,后续随笔再做详细介绍:这个强大的Task类,还有很多值得我们去探索的东西。

本随笔到此、暂告一段落。

附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

参见更多:随笔导读:同步与异步

(未完待续...)

.NET 实现并行的几种方式(一)的更多相关文章

  1. .NET 实现并行的几种方式(四)

    本随笔续接:.NET 实现并行的几种方式(三) 八.await.async - 异步方法的秘密武器 1) 使用async修饰符 和 await运算符 轻易实现异步方法 前三篇随笔已经介绍了多种方式.利 ...

  2. .NET 实现并行的几种方式(三)

    本随笔续接:.NET 实现并行的几种方式(二) 在前两篇随笔中,先后介绍了 Thread .ThreadPool .IAsyncResult (即 APM系列) .Task .TPL (Task Pa ...

  3. .NET 实现并行的几种方式(二)

    本随笔续接:.NET 实现并行的几种方式(一) 四.Task 3)Task.NET 4.5 中的简易方式 在上篇随笔中,两个Demo使用的是 .NET 4.0 中的方式,代码写起来略显麻烦,这不 .N ...

  4. Oracle并行更新的两种方式(merge/update内联视图)

    对于Oracle的两表联合更新的场景(有A.B两表,以A.id=B.id关联,根据B表中的记录更新A表中的相应字段),一般有update内联视图和merge两种方式,下面举例介绍:   创建用例表: ...

  5. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  6. ios多线程开发的常用三种方式

    1.NSThread 2.NSOperationQueue 3.GCD NSThread: 创建方式主要有两种: [NSThread detachNewThreadSelector:@selector ...

  7. JAVA解析XML的四种方式

    java解析xml文件四种方式 1.介绍 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这 ...

  8. 创建线程(Background Thread)的N种方式

    第一.Thread类 Thread类是实例化线程的主要方法:一个Thread实例管理一个线程,即执行序列.通过简单实例化一个对象,就可以创建一个线程,然后通过Thread对象提供的方法对线程进行管理. ...

  9. 让浏览器非阻塞加载javascript的几种方式

    通常大多数浏览器是并行下载资源的,但由于外部脚本的特殊性例如通过脚本改变文档的DOM结构.脚本之间的存在依赖关系.使用document.write 向页面输出HTML等.浏览器为了确保正确执行脚本和呈 ...

随机推荐

  1. scala练习题1 基础知识

    1, 在scala REPL中输入3. 然后按下tab键,有哪些方法可以被调用? 24个方法可以被调用, 8个基本类型: 基本的操作符, 等:     2,在scala REPL中,计算3的平方根,然 ...

  2. 【夯实PHP基础】PHP数组,字符串,对象等基础面面观

    本文地址 分享提纲 1.数组篇 2.字符创篇 3.函数篇 4.面向对象篇 5.其他篇 /*************************** 一.数组篇 Begin***************** ...

  3. GOF23设计模式归类

    创建型模式:-单例模式.工厂模式.抽象工厂模式.建造者模式.原型模式结构型模式:-适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式行为型模式:-模板方法模式.命令模式.迭代器模式 ...

  4. JavaScript学习笔记(三)——this、原型、javascript面向对象

    一.this 在JavaScript中this表示:谁调用它,this就是谁. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是一个动态的对象,根据调用的对象不同而发生变化, ...

  5. H3 BPM让天下没有难用的流程之功能介绍

    H3 BPM10.0功能地图如下:  图:H3 BPM 功能地图 一.流程引擎 H3  BPM 流程引擎遵循WFMC 标准的工作流引擎技术,设计可运行的流程和表单,实现工作任务在人与人.人与系统.系统 ...

  6. atitit.管理学三大定律:彼得原理、墨菲定律、帕金森定律

    atitit.管理学三大定律:彼得原理.墨菲定律.帕金森定律 彼得原理(The Peter Principle) 1 彼得原理解决方案1 帕金森定律 2 如何理解墨菲定律2 彼得原理(The Pete ...

  7. 【MySql】查询数据库中所有表及列的信息

    SELECT TABLE_NAME, -- 表名 COLUMN_NAME, -- 字段名 DATA_TYPE, -- 字段类型 COLUMN_COMMENT -- 字段注释 FROM INFORMAT ...

  8. mono3.2和monodevelop4.0在ubuntu12.04上两天的苦战

    首先第一步是设置ubuntu server 12.04版更新源,推荐中科大的比较快:deb http://debian.ustc.edu.cn/ubuntu/ precise main multive ...

  9. NodeJs 开发微信公众号(四)微信网页授权

    微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理.个人等信息的权限.对于开发了自己的网页app应用时,获取个人的信息非常重要.上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需 ...

  10. UWP开发:APP之间的数据交互(以微信为例)

    目录 说明 UWP应用唤醒方式 跟微信APP交互数据 APP之间交互数据的前提 说明 我们经常看到,在手机上不需要退到桌面,APP之间就可以相互切换,并且可以传递数据.比如我在使用知乎APP的时候,需 ...