主要分为八个部分:1.线程Thread;2.后台线程BackGroundWorker;3.互斥锁Monitor、Lock、Mutex;4.线程池ThreadPool;5.线程间通信WaitHandle、ManualResetEvent、AutoResetEvent和Barrier;6.任务Task、TaskFactory和Parallel;7.异步委托;8.其他,UI多线程,Timer等;
首先最常用的莫过于Thread了,运用Thread.Sleep(1000);就可以让当前线程休眠1000毫秒;创建一个线程Thread thread=new Thread(action);其中action可以是一个委托或方法名,然后thread.Start();就可以了;默认情况下创建的线程属于前台线程;代码如下:
using System; using System.Threading; namespace ThreadDemo { public class Thread0810 { public static void Run() { CheckBackground(); Console.ReadLine(); } private static void CheckBackground() { bool isBackground = true; Thread thread = new Thread(() => { Console.WriteLine("Hello Thread!"); }); isBackground = thread.IsBackground; thread.Start(); Thread.Sleep(1 * 1000); Console.WriteLine("IsBackground:" + isBackground.ToString()); } } }
其次就是后台线程BackGroundWorker了,顾名思义后台线程默认的线程是后台的;前台线程和后台线程的区别在于后台线程在所有的前台线程都结束时会被强制结束;可以注册DoWork事件(注意当注册多个时,事件是一个个的顺序执行,事件绑定多个方法时就是这么执行的),可以设置后台线程是否可以取消;可以设置报告后台线程的进度(需要在执行的方法中调用ReportProgress方法)和线程执行完成;ProgressChanged报告后台线程的事件,RunWorkerCompleted事件报告线程执行完毕的结果;简单的示例代码例子如下:
using System; using System.ComponentModel; using System.Threading; namespace ThreadDemo { class BackgroudWorker0810 { public static void Run() { TestBackgroudWorker(); Console.ReadLine(); } private static void TestBackgroudWorker() { Console.WriteLine("BackgroundWorker Begin"); BackgroundWorker back = new BackgroundWorker(); back.DoWork += new DoWorkEventHandler(BackgroundWorkerTest); back.RunWorkerAsync(); } private static void BackgroundWorkerTest(object sender, DoWorkEventArgs es) { Thread.Sleep(1 * 1000); Console.WriteLine("Hello BackgroundWorker!"); } } }包含完整功能的例子代码:
using System; using System.ComponentModel; using System.Threading; namespace ThreadDemo { class BackgroudWorker0810Ex { private static BackgroundWorker back = new BackgroundWorker(); public static void Run() { TestBackgroudWorker(); Console.ReadLine(); } private static void TestBackgroudWorker() { back.WorkerSupportsCancellation = true; back.WorkerReportsProgress = true; back.DoWork += ExecuteMethod; back.ProgressChanged += BackProgressChanged; back.RunWorkerCompleted += BackWorkerCompleted; back.RunWorkerAsync(); if (back.CancellationPending == false) { Console.WriteLine("BackgroundWorker Still Run"); } else { Console.WriteLine("BackgroundWorker is Stop"); } Thread.Sleep(5 * 1000); back.DoWork -= ExecuteMethod; back.CancelAsync(); if (back.CancellationPending == false) { Console.WriteLine("BackgroundWorker Still Run"); } else { Console.WriteLine("BackgroundWorker is Stop"); } } static void ExecuteMethod(object sender, DoWorkEventArgs es) { for (int i = 0; i < 20; i++) { if (back.CancellationPending == false) { Thread.Sleep(1000); back.ReportProgress(i, i * 100); } else { break; } } } static void BackProgressChanged(object sender, ProgressChangedEventArgs e) { Console.WriteLine("UserState:" + e.UserState.ToString() + "***ProgressPercentage:" + e.ProgressPercentage.ToString()); } static void BackWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Console.WriteLine("BackWorkerCompleted"); } } }
用到多线程,常常就会牵扯到资源,此时就需要考虑线程安全性常用的是Monitor和Lock,其中Lock是C#的一个语法糖,Lock是对Monitor进行的封装,Monitor要注意的是操作过程中在finnally中一定要释放锁的资源(不能因为出异常而将资源一直锁着),Mutex与Monitor类似;线程安全性,就是在多线程情况下可能出现不一致的情况,简单的说就是多线程时执行结果的不确定性或者错误。
不安全的示例代码如下:
using System; using System.Threading; namespace ThreadDemo { class UnSafeCode0810 { public static void Run() { TestUnSafe(); Console.ReadLine(); } private static int tempArg = 100; private static void TestUnSafe() { //两个线程都进行给TempArg加一的操作 //两个线程同时进入到那段代码,实际值增加1(而不是2) ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); } private static void AddValue(Object state) { Console.WriteLine("OldValue:" + tempArg.ToString()); int i = tempArg; //这边实际有一系列的操作(这边就直接加一) i++; //耗时操作(这边就直接Sleep两秒) Thread.Sleep(2000); tempArg = i; Console.WriteLine("NewValue:" + tempArg.ToString()); } } }Monitor示例代码:
using System; using System.Threading; namespace ThreadDemo { class MonitorCode0810 { private static int tempArg = 100; private static readonly object obj = new object(); public static void Run() { TestMonitor(); Console.ReadLine(); } private static void TestMonitor() { //两个线程都进行给TempArg加一的操作,由于方法里加了锁 //解决了无锁时出现的问题,实际值增加2 ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); } private static void AddValue(Object state) { try { Monitor.Enter(obj); Console.WriteLine("OldValue:" + tempArg.ToString()); int i = tempArg; //这边实际有一系列的操作(这边就直接加一) i++; //耗时操作(这边就直接Sleep两秒) Thread.Sleep(2000); tempArg = i; Console.WriteLine("NewValue:" + tempArg.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { Monitor.Exit(obj); } } } }Lock示例代码:
using System; using System.Threading; namespace ThreadDemo { class LockCode0810 { public static void Run() { TestLock(); Console.ReadLine(); } private static int tempArg = 100; private static readonly object obj = new object(); private static void TestLock() { //两个线程都进行给TempArg加一的操作 //由于方法里加了锁,解决了无锁时出现的问题,实际值增加2 ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); } private static void AddValue(Object state) { lock (obj) { Console.WriteLine("OldValue:" + tempArg.ToString()); int i = tempArg; //这边实际有一系列的操作(这边就直接加一) i++; //耗时操作(这边就直接Sleep两秒) Thread.Sleep(2000); tempArg = i; Console.WriteLine("NewValue:" + tempArg.ToString()); } } } }Mutex示例代码:
using System; using System.Threading; namespace ThreadDemo { class MutexCode0810 { private static int tempArg = 100; private static Mutex mut = new Mutex(); public static void Run() { TestMonitor(); Console.ReadLine(); } private static void TestMonitor() { //两个线程都进行给TempArg加一的操作 //由于方法里加了锁,解决了无锁时出现的问题,实际值增加2 ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); ThreadPool.QueueUserWorkItem(new WaitCallback(AddValue)); } private static void AddValue(Object state) { try { mut.WaitOne(); Console.WriteLine("OldValue:" + tempArg.ToString()); int i = tempArg; //这边实际有一系列的操作(这边就直接加一) i++; //耗时两秒(这边就直接Sleep两秒) Thread.Sleep(2000); tempArg = i; Console.WriteLine("NewValue:" + tempArg.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } finally { mut.ReleaseMutex(); } } } }
线程池,首先线程池是为了解决当程序有若干独立小任务执行时反复创建和销毁线程带来的性能损耗;将任务放到线程池中,线程执行完并不回收线程,降低了新建线程的开销,重复利用线程;默认情况下线程池会根据CPU的个数开辟相应的线程,其他的任务则在等待(例如4核CPU,12个线程请求进入,则会处理先进入的4个线程,另外8个则在等待之中,直到有线程执行完毕);如果同时有大量线程涌入,不会立即开启线程,而是等待一定时间间隔再新开一个线程(本机测试时是1s,本机是笔记本,cpu是i5,内存8g,64位系统),以此类推;如果要扩展默认同时处理的线程数可以设置指定个数的线程。示例代码如下:
using System; using System.Threading; namespace ThreadDemo { class ThreadPool0810 { public static void Run() { for (int i = 0; i < 20; i++) { ThreadPool.QueueUserWorkItem(TestMethod, i); } Thread.Sleep(3 * 1000); Console.WriteLine("******LongTimeMethod******"); for (int i = 0; i < 10; i++) { ThreadPool.QueueUserWorkItem(LongTimeMethod, i); } Thread.Sleep(15 * 1000); Console.WriteLine("******LongTimeMethodEx******"); ThreadPool.SetMinThreads(10, 0); for (int i = 0; i < 20; i++) { ThreadPool.QueueUserWorkItem(LongTimeMethodEx, i); } Console.ReadLine(); } static void TestMethod(Object stateInfo) { Thread.Sleep(500); Console.WriteLine("****{0}****,{1}", stateInfo.ToString(), DateTime.Now.ToString("HH:mm:ss.fff")); } static void LongTimeMethod(Object stateInfo) { Console.WriteLine("****{0}****Begin****,{1}", stateInfo.ToString(), DateTime.Now.ToString("HH:mm:ss.fff")); Thread.Sleep(6 * 1000); Console.WriteLine("****{0}***End****,{1}", stateInfo.ToString(), DateTime.Now.ToString("HH:mm:ss.fff")); } static void LongTimeMethodEx(Object stateInfo) { Console.WriteLine("****{0}****Begin****,{1}", stateInfo.ToString(), DateTime.Now.ToString("HH:mm:ss.fff")); Thread.Sleep(10 * 1000); Console.WriteLine("****{0}****End****,{1}", stateInfo.ToString(), DateTime.Now.ToString("HH:mm:ss.fff")); } } }
WaitHandle ,WaitAll等所有的都执行完,WaitAny等待最快的执行完。示例代码如下:
using System; using System.Threading; namespace ThreadDemo { class WaitHandle0810 { private static WaitHandle[] handles = new WaitHandle[] { new AutoResetEvent(false), new AutoResetEvent(false), new AutoResetEvent(false) }; public static void Run() { Console.WriteLine("WaitAll Begin" + DateTime.Now.ToString("hh:mm:ss.fff")); ThreadPool.QueueUserWorkItem(new WaitCallback(test1), handles[0]); ThreadPool.QueueUserWorkItem(new WaitCallback(test2), handles[1]); ThreadPool.QueueUserWorkItem(new WaitCallback(test3), handles[2]); WaitHandle.WaitAll(handles); Console.WriteLine("WaitAll End" + DateTime.Now.ToString("hh:mm:ss.fff")); Console.WriteLine("WaitAny Begin" + DateTime.Now.ToString("hh:mm:ss.fff")); ThreadPool.QueueUserWorkItem(new WaitCallback(test1), handles[0]); ThreadPool.QueueUserWorkItem(new WaitCallback(test2), handles[1]); ThreadPool.QueueUserWorkItem(new WaitCallback(test3), handles[2]); WaitHandle.WaitAny(handles); Console.WriteLine("WaitAny End" + DateTime.Now.ToString("hh:mm:ss.fff")); Console.ReadLine(); } private static void test1(Object state) { AutoResetEvent are = (AutoResetEvent)state; Thread.Sleep(3 * 1000); are.Set(); } private static void test2(Object state) { AutoResetEvent are = (AutoResetEvent)state; Thread.Sleep(4 * 1000); are.Set(); } private static void test3(Object state) { AutoResetEvent are = (AutoResetEvent)state; Thread.Sleep(5 * 1000); are.Set(); } } }ManualResetEvent,阻塞相关的所有线程,直至释放,释放时会释放所有的线程,释放完标志位无法自动重置(此时不再阻塞线程),需手动reset才可以继阻塞;示例代码如下:
using System; using System.Threading; namespace ThreadDemo { class ManualResetEvent0810 { static ManualResetEvent mre = new ManualResetEvent(false); public static void Run() { ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod01)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod02)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod03)); Thread.Sleep(2 * 1000); Console.WriteLine("***************************************"); mre.Set(); Thread.Sleep(2 * 1000); Console.WriteLine("***************************************"); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod01)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod02)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod03)); Thread.Sleep(2 * 1000); Console.WriteLine("***************************************"); mre.Reset(); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod01)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod02)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod03)); Thread.Sleep(2 * 1000); Console.WriteLine("***************************************"); mre.Set(); Thread.Sleep(2 * 1000); mre.Reset(); Console.WriteLine("***************************************"); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod04)); Console.ReadLine(); } private static void TestMethod01(Object state) { Console.WriteLine("test1 Begin Wait," + DateTime.Now.ToString("hh:mm:ss")); mre.WaitOne(); Console.WriteLine("test1 End Wait," + DateTime.Now.ToString("hh:mm:ss")); } private static void TestMethod02(Object state) { Console.WriteLine("test2 Begin Wait," + DateTime.Now.ToString("hh:mm:ss")); mre.WaitOne(); Console.WriteLine("test2 End Wait," + DateTime.Now.ToString("hh:mm:ss")); } private static void TestMethod03(Object state) { Console.WriteLine("test3 Begin Wait," + DateTime.Now.ToString("hh:mm:ss")); mre.WaitOne(); Console.WriteLine("test3 End Wait," + DateTime.Now.ToString("hh:mm:ss")); } private static void TestMethod04(Object state) { Console.WriteLine("TestMethod04 Begin Wait," + DateTime.Now.ToString("hh:mm:ss")); mre.WaitOne(2*1000); Console.WriteLine("TestMethod04 End Wait," + DateTime.Now.ToString("hh:mm:ss")); } } }AutoResetEvent,一次释放一个线程,且释放具有随机性(没有先后顺序),释放完会立即重置,为false时阻塞,为true时释放,释放完自动置为false;示例代码如下:
using System; using System.Threading; namespace ThreadDemo { class AutoResetEvent0810 { static AutoResetEvent autoResetEvent = new AutoResetEvent(false); public static void Run() { for (int i = 1; i <= 3; i++) { Thread t = new Thread(TestMethod); t.Name = "Thread" + i; t.Start(); } for (int i = 1; i <= 3; i++) { Thread.Sleep(i * 1000); autoResetEvent.Set(); } Console.ReadLine(); } private static void TestMethod() { string currentThreadName = Thread.CurrentThread.Name; Console.WriteLine("{0} Begin Wait,{1}", currentThreadName, DateTime.Now.ToString("hh:mm:ss.fff")); autoResetEvent.WaitOne(); Console.WriteLine("{0} End Wait,{1}", currentThreadName, DateTime.Now.ToString("hh:mm:ss.fff")); } } }Barrier,一次阻塞指定个数的线程(例如值为n),当有n个线程发出信号量时释放线程一次执行相应的操作,然后立即再次阻塞,直至又有累积n个线程发出信号量时才释放;这个思想在Log4net的数据库模式中有体现,log4net配置日志记录到数据库时可以设置当日志达到多少条时一次性插入。示例代码如下:
using System; using System.Threading; namespace ThreadDemo { public class Barrier0810 { static Barrier barrier = new Barrier(2, (b) => { Console.WriteLine("已经累积两个任务了,特此通告"); }); public static void Run() { ThreadPool.SetMinThreads(6, 0); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod01)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod02)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod03)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod04)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod05)); ThreadPool.QueueUserWorkItem(new WaitCallback(TestMethod06)); Console.ReadLine(); } private static void TestMethod01(Object state) { Thread.Sleep(1 * 1000); Console.WriteLine("test1" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } private static void TestMethod02(Object state) { Thread.Sleep(2 * 1000); Console.WriteLine("test2" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } private static void TestMethod03(Object state) { Thread.Sleep(3 * 1000); Console.WriteLine("test3" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } private static void TestMethod04(Object state) { Thread.Sleep(1 * 1000); Console.WriteLine("TestMethod04" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } private static void TestMethod05(Object state) { Thread.Sleep(2 * 1000); Console.WriteLine("TestMethod05" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } private static void TestMethod06(Object state) { Thread.Sleep(3 * 1000); Console.WriteLine("TestMethod06" + DateTime.Now.ToString("hh:mm:ss")); barrier.SignalAndWait(); } } }
Task和TaskFactory,可以认为是线程和线程池的改良版(就跟Dictionary是HashTable的改良版一样),Task的内部采用线程池实现(针对多核进行了优化),Task可以中途取消,可以进行后继任务;默认情况Task是后台线程;Parallel对Task的一种封装,为常见循环操作 (For 循环、For each 循环)实现并行操作。
证实Task内部采用线程池的代码:
using System; using System.Threading.Tasks; using System.Threading; namespace ThreadDemo { class Task0810 { public static void Run() { ShowThreadPoolInfo("Task Begin"); Task task1 = new Task(TestMethod); task1.Start(); Task task2 = new Task(TestMethod); task2.Start(); Task task3 = new Task(TestMethod); task3.Start(); ShowThreadPoolInfo("Task End"); Console.ReadLine(); } private static void ShowThreadPoolInfo(string info) { int workerThreads; int threads; ThreadPool.GetAvailableThreads(out workerThreads, out threads); Console.WriteLine("{0},workerThreads:{1},threads:{2},{3}", info, workerThreads, threads, DateTime.Now.ToString("HH:mm:ss.fff")); } private static void TestMethod() { Thread.Sleep(1000); ShowThreadPoolInfo("TestMethod Run"); } } }取消线程的示例代码:
using System; using System.Threading; using System.Threading.Tasks; namespace ThreadDemo { class TaskCancel0810 { private static CancellationTokenSource ct; public static void Run() { Console.WriteLine("Begin"); ct = new CancellationTokenSource(); Task task = new Task((() => TestMethod(ct))); task.Start(); Thread.Sleep(5 * 1000); ct.Cancel(); Console.ReadLine(); } private static void TestMethod(CancellationTokenSource ct) { for (int i = 0; i < 10; i++) { if (!ct.IsCancellationRequested) { Thread.Sleep(1 * 1000); Console.WriteLine(DateTime.Now.ToString()); } else { Console.WriteLine("Cancel"); break; } } } } }后继任务、获得任务结果的示例代码:
using System; using System.Threading.Tasks; namespace ThreadDemo { class TaskContinue0810 { public static void Run() { Task<string> taskFirst = new Task<string>(TestFirst); taskFirst.Start(); Task<string> taskContinue = taskFirst.ContinueWith((t) => { Console.WriteLine(t.Result); return TestContinue(); }); taskContinue.ContinueWith((t) => { Console.WriteLine(t.Result); }); Console.ReadLine(); } private static string TestFirst() { return "TestFirst"; } private static string TestContinue() { return "TestContinue"; } } }Parallel的示例代码:
using System; using System.Threading.Tasks; using System.Threading; namespace ThreadDemo { class Parallel0810 { private static int N = 20; public static void Run() { Parallel.For(0, N, TestMethod); Console.ReadLine(); } private static void TestMethod(int i) { Console.WriteLine(i.ToString() + "******" + DateTime.Now.ToString("HH:mm:ss.fff")); Thread.Sleep(1 * 1000); } } }
异步委托提供以异步方式调用同步方法的能力。示例代码如下:
using System; using System.Threading; using System.Runtime.Remoting.Messaging; namespace ThreadDemo { class AsyncResult0810 { public static void Run() { IAsyncResult asyn1 = new Action(Task1) .BeginInvoke(new AsyncCallback(Resule1), " 任务一"); IAsyncResult asyn2 = new Func<int, Person>(Task2) .BeginInvoke(100, new AsyncCallback(Resule2), " 任务二"); } private static void Task1() { Thread.Sleep(1 * 1000); Console.WriteLine("任务一完成!"); } private static Person Task2(int value) { Thread.Sleep(2 * 1000); Console.WriteLine("任务二完成!"); return new Person(); } /// <summary> /// 异步操作完成后调用的方法 /// </summary> /// <param currentThreadName="result"></param> private static void Resule1(IAsyncResult result) { string msg = result.AsyncState.ToString(); Console.WriteLine("这是耗时操作完成标志,额外信息是:{0}", msg); Console.WriteLine("没有异步返回值。"); } /// <summary> /// 异步操作完成后调用的方法 /// </summary> /// <param currentThreadName="result"></param> private static void Resule2(IAsyncResult result) { Func<int, Person> asyncDelegate = (result as AsyncResult).AsyncDelegate as Func<int, Person>; string msg = result.AsyncState.ToString(); ///person是异步返回的结果 Person person = asyncDelegate.EndInvoke(result); Console.WriteLine("这是耗时操作完成标志,额外信息是:{0}", msg); Console.WriteLine("异步返回值:currentThreadName{0},age{1}", person.name, person.age); } /// <summary> /// 用户类(用于测试) /// </summary> private class Person { public string name = "pfe_Nova"; public int age = 22; } } }
Winform中简单的常用Application.DoEvents()实现,WPF中常用Dispatcher.Run()实现;Timer一般用于定时周期性的操作,Timer示例代码如下:
using System; using System.Timers; using System.Threading; namespace ThreadDemo { class Timer0810 { static readonly System.Timers.Timer timer = new System.Timers.Timer(); public static void Run() { TestTimer(); Console.ReadLine(); } private static void TestTimer() { ShowThreadPoolInfo("TestTimer"); timer.Enabled = false; SetInterval(); timer.Elapsed += timerElapsed; timer.AutoReset = false; timer.Start(); ShowThreadPoolInfo("TestTimer"); } private static void SetInterval() { timer.Interval = 12 * 1000; } private static void timerElapsed(object sender, ElapsedEventArgs e) { Console.WriteLine("timerElapsed" + DateTime.Now.ToString("hh:mm:ss.fff")); Thread.Sleep(5000); timer.Stop(); SetInterval(); timer.Start(); Console.WriteLine("timerElapsed start" + DateTime.Now.ToString("hh:mm:ss.fff")); } private static void ShowThreadPoolInfo(string info) { int workerThreads; int num; ThreadPool.GetAvailableThreads(out workerThreads, out num); Console.WriteLine("{0},workerThreads:{1},portThreads:{2},{3}", info, workerThreads, num, DateTime.Now.ToString("HH:mm:ss.fff")); } } }
其他的诸如Thread.Sleep直接调用内核的指令,所在线程挂起,CPU执行队列的重排序,Thread.Sleep(0)的意义在于重新分配,而不是等待了。
至此全部完成,只是个人的一些理解,对自己是一个记录,同时希望也能对别人有些帮助,如果有什么错误,还望不吝指教。转载请保留原文链接。
博客源码下载