大话异步与并行(二)
接着上期的文章继续说异步与并行
并行来自于线程的方法实现,异步不一定。这句话,晕倒一大片程序员。
首先,多线程序是实现异步一种方法,两者的共同目的:使主线程保持对用户操作的实时响应,如点击、拖拽、输入字符等。使主程序看起来实时都保持着等待用户响应的状态,而后台却有若干件事情在自己干。按消耗资源所在地可分为两类:硬件异步类和CPU异步类。
硬件异步的特点:将需要在后台执行的操作甩给底层硬件去执行,不占用线程和CPU资源。所以说,并不是所有的异步都占有线程的。
硬件异步类大概有以下这几类。
应用程序范围 |
支持包含异步方法的 API |
Web 访问 |
|
处理文件 |
|
使用图像处理 |
|
WCF 编程 |
|
与套接字处理 |
CPU常用的异步方式、方法
1、独立的线程—ThreadStart
一般情况下,要为不会阻止其他线程的相对较短的任务处理多个线程并且不需要对这些任务执行任何特定调度时,使用 ThreadPool 类是一种最简单的方式。 但是,有多个理由创建您自己的线程:
如果您需要使一个任务具有特定的优先级。
如果您具有可能会长时间运行(并因此阻止其他任务)的任务。
如果您需要将线程放置到单线程单元中(所有 ThreadPool 线程均处于多线程单元中)。
如果您需要与该线程关联的稳定标识。 例如,您应使用一个专用线程来中止该线程,将其挂起或按名称发现它。
如果您需要运行与用户界面交互的后台线程,.NET Framework 2.0 版提供了 BackgroundWorker 组件,该组件可以使用事件与用户界面线程的跨线程封送进行通信。
2、ThreadPool—ThreadPool.QueueUserWorkItem(M())
3、任务,Task系列--普通任务、关联的任务(Task<T>.ContinueWith(…))、父子任务、任务工厂(TaskTactory<TResult>)
4、Parallel静态类--- System.Threading.Tasks.Parallel.For(…) System.Threading.Tasks.Parallel.ForEach(…) Parallel.Invoke(() => Sort());
5、PLINQ
6、定时器
到此只是简单的基础知识阐述。如果不太清楚,下面的陆续的文章将会一一讲起。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Thread类(线程类)
除了使用委托创建线程之外,还可以使用thread 类创建线程
static void Main(string[] args) { Thread t = new Thread(ThreadMain); t.Start(); Console.WriteLine("This ia a mian thread."); } static void ThreadMain() { Console.WriteLine("Running in a thread."); }
简化以上代码
static void Main(string[] args) { new Thread(() => Console.WriteLine("Running in a thread.") ).Start(); Console.WriteLine("This ia a mian thread."); }
再次见证 拉姆达(lambda)表达式与匿名方法 的威力。
在上面简单的Thread类就创建并开始了一个线程。Thread类默认的是 IsBackground =false,也就是说它是前台线程。
说到前台线程与后台线程,上一文章提到,当前台进程停止的时候,后台进程也将停止,当时是放在mian主线程测试的,必须关闭或结束主线程,看的不是太清楚
现在有了Thread类,下面的例子将会开启不依懒主线程的测试。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { DateTime now = DateTime.Now; Thread t1 = new Thread(() => { Console.WriteLine("Running in a thread t1."); Func<decimal, int, decimal> f = (money, ms) => { Console.WriteLine("SaveBankAccountPersonA thread started! current run at threadID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("SaveBankAccountPersonA thread IsBackground " + Thread.CurrentThread.IsBackground); Thread.Sleep(ms); Console.WriteLine("SaveBankAccountPersonA thread completed!"); return ++money; }; var ar = f.BeginInvoke(1, 200, (r) => { if (r == null) { throw new ArgumentNullException("r"); } Thread.Sleep(1000); Console.WriteLine("AsycyCallBackCurrentMoneyPersonA:{0}", f.EndInvoke(r)); Console.WriteLine("AsycyCallBackRunTimePersonA:{0}", (DateTime.Now - now).TotalSeconds); Console.WriteLine("AsycyCallBackSaveBankAccountPersonA thread IsBackground " + Thread.CurrentThread.IsBackground); }, null); while (!ar.IsCompleted) { Console.WriteLine("threadT1 wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); } }); Thread t2 = new Thread(() => { Console.WriteLine("Running in a thread t2."); Func<decimal, int, decimal> f = (money, ms) => { Console.WriteLine("SaveBankAccountPersonB thread started! current run at threadID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("SaveBankAccountPersonB thread IsBackground " + Thread.CurrentThread.IsBackground); Thread.Sleep(ms); Console.WriteLine("SaveBankAccountPersonB thread completed!"); return ++money; }; var ar = f.BeginInvoke(1, 200, (r) => { if (r == null) { throw new ArgumentNullException("r"); } Console.WriteLine("AsycyCallBackCurrentMoneyPersonB:{0}", f.EndInvoke(r)); Console.WriteLine("AsycyCallBackRunTimePersonB:{0}", (DateTime.Now - now).TotalSeconds); Console.WriteLine("AsycyCallBackSaveBankAccountPersonB thread IsBackground " + Thread.CurrentThread.IsBackground); }, null); while (!ar.IsCompleted) { Console.WriteLine("threadT2 wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); } }); t1.Start(); t2.Start(); t1.Abort(); Console.WriteLine("This ia a mian thread."); Console.ReadKey(); } } }
上面的代码很多,对上一文章的进行扩展。分别启用两个线程t1和t2 ,并在每个线程里加入异步委托A和B,从而开始新的后台线程(异步委托默认是后台线程)
上面这张图,t1还没有来及运行,就已停止,下面这张图,t1运行起来了
但是A还是没有运行起来,充分说明A一并被t1停止
后台线程A和B 同时拥有两个回调函数 ,在A回调函数里 加入了Sleep(1000) 延迟1秒,紧接着外面t1.Abort();结束前台线程t1。从而达到,外面的t1前台线程结束时后台线程A还没有来及结束(实际上已强制性并随t1前台线程结束了!)
这样就验证了,前台线程结束所依懒的后台线程所并随结束的事实!代码就是最好的说明
小结:本节在文章一节中着重阐述的前台线程与后台线程作了在Thread类基础上做了实例论证,从而证明,后台线程的生命周期由依懒的前台线程结束而结束。
同时也将异步与多线程进一步举例说明,线程只是异步的一种实现方法!