16.1 线程的概念
16.2 线程的基本操作
16.2.1 创建新线程
Thread th = new Thread()
线程有以下几个常用的属性:
ManagedThreadId属性,托管线程ID,在进程内唯一
name属性,默认为空
ThreadState属性,是一个位标记,描述了线程的当前运行状态:Background、Unstarted、Running、WaitSleepJoin、Stopped。
16.2.2 查看当前线程
Thread.CurrentThread.Name
16.2.3 Sleep()方法
public static void Sleep(int millisecondsTimeout);
public static void Sleep(TimeSpan timeout);
16.2.4 Interrupt()方法
16.2.5 前台线程和后台线程
前台后台区别:所有前台线程执行完毕后,应用程序进程结束,而无论后台进程是否结束。
容易混淆的一个地方:后台线程的background标记就相当于前台线程的running标记,而不是background,running标记
在创建一个线程之后,就应该对它的生存周期有完全的掌控,因此,不去管理后台线程,让它随主线程的结束而终结是很欠妥的做法,尤其是当后台线程还持有一些资源需要关闭时。当后台线程以这种方式退出时,即使位于finally块中的语句也不会执行。
16.2.6 Join()方法
等待线程执行结束后,在继续执行后面的代码
16.2.7 Suspend()和Resume()方法
用于挂起和继续执行已挂起的线程(不推荐使用)
16.2.8 线程异常
当工作线程抛出异常时,整个进程都会关闭,而不是仅结束抛出异常的线程(调用abort方法抛出ThreadAbortException异常除外)
16.2.9 Abort()方法
如果想要强制退出一个线程,可以调用Abort()方法,调用Abort()方法会抛出一个System.Threading.ThreadAbortException异常,该异常很特殊,因为即使不捕获它,也不会影响到整个进程。
16.3 线程同步
线程同步就是协调多个线程间的并发操作,以获得预期的确定的执行结果,消除多线程应用程序执行中的不确定性,包含两方面:
1、保护资源,确保资源同时只能有一个(或指定个数)的线程访问,一般措施是获取锁和释放锁(锁机制)
2、协调线程对资源的访问顺序,即确定某一资源只能现有线程A访问,再由线程B访问,一般措施是采用信号量机制。当B线程访问资源时,必须等待线程A先访问,线程A访问完后,发出信号量,通知线程B可以访问
16.3.1 使用Monitor
1.使用对象本身作为锁对象
Monitor只能对引用类型加锁,否则会抛错
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; namespace ConsoleApplication1 { class Program { Resource res = new Resource(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; th.Start(); p.ThreadEntity(); Console.Read(); } void ThreadEntity() { Monitor.Enter(res); res.Record(); Monitor.Exit(res); } } public class Resource { public string call; public void Record() { call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond); Console.WriteLine(call); } } }
2.使用System.Object作为锁对象
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; namespace ConsoleApplication1 { class Program { Resource res = new Resource(); private object lockObject = new object(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; th.Start(); p.ThreadEntity(); Console.Read(); } void ThreadEntity() { Monitor.Enter(lockObject); res.Record(); Monitor.Exit(lockObject); } } public struct Resource { public string call; public void Record() { call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond); Console.WriteLine(call); } } }
3.使用System.Type作为锁对象
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; namespace ConsoleApplication1 { class Program { private object lockObject = new object(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; th.Start(); p.ThreadEntity(); Console.Read(); } void ThreadEntity() { Monitor.Enter(typeof(Resource)); Resource.Record(); Monitor.Exit(typeof(Resource)); } } public static class Resource { public static string call; public static void Record() { call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond); Console.WriteLine(call); } } }
4.使用lock语句
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; namespace ConsoleApplication1 { class Program { Resource res = new Resource(); private object lockObject = new object(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; //th.IsBackground = true; th.Start(); p.ThreadEntity(); Console.Read(); } void ThreadEntity() { lock (res) { try { Resource.Record(); } catch(Exception ex) { Console.WriteLine(ex.Message); } } } } public class Resource { public static string call; public static void Record() { call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond); Console.WriteLine(call); throw new Exception(); } } }
5.创建线程安全类型
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; using System.Runtime.CompilerServices; namespace ConsoleApplication1 { class Program { Resource res = new Resource(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; //th.IsBackground = true; th.Start(); p.ThreadEntity(); Console.Read(); } void ThreadEntity() { res.Record(); } } public class Resource { public string call; [MethodImpl(MethodImplOptions.Synchronized)] public void Record() { call += string.Format("{0}:{1}", Thread.CurrentThread.Name, DateTime.Now.Millisecond); Console.WriteLine(call); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; using System.Runtime.CompilerServices; namespace ConsoleApplication1 { class Program { static Resource res = new Resource(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; //th.IsBackground = true; th.Start(); p.ThreadEntity(); //Console.WriteLine(res.Index); Console.Read(); } void ThreadEntity() { lock (res) { for (int i = 0; i <= 2; i++) { lock (res) { res.Index = res.Index + 1; Console.WriteLine("{0}:{1}", Thread.CurrentThread.Name, res.Index); } } } } } public class Resource { private int _index; public int Index { get { lock (this) { return _index; } } set { lock (this) { _index = value; } } } } }
锁的粒度对于程序执行的顺序和结果是很重要的。
6.使用Monitor协调线程执行顺序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; using System.Runtime.CompilerServices; namespace ConsoleApplication1 { class Program { Resource res = new Resource(); static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.ThreadEntity); th.Name = "Worker"; //th.IsBackground = true; th.Start(); //Thread.Sleep(1000); lock (p.res) { if (string.IsNullOrEmpty(p.res.data)) { bool isTimeout = Monitor.Wait(p.res, 100); Console.WriteLine(isTimeout); } Console.WriteLine("data={0}", p.res.data); } //Console.WriteLine(res.Index); Console.Read(); } void ThreadEntity() { lock (res) { res.data = "Retrived"; Monitor.Pulse(res); } } } public class Resource { public string data; } }
7.死锁
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; using System.Runtime.CompilerServices; namespace ConsoleApplication1 { class Program { Resource mainRes = new Resource() { data = "Main" }; Resource workerRes = new Resource() { data = "Worker" }; static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.T2); th.Name = "Worker"; //th.IsBackground = true; th.Start(); T1(p); Console.Read(); } static void T1(Program p) { lock (p.mainRes) { Thread.Sleep(10); lock (p.workerRes) { Console.WriteLine(p.workerRes.data); } } } void T2() { lock (workerRes) { Thread.Sleep(10); lock (mainRes) { Console.WriteLine(mainRes.data); } } } } public class Resource { public string data; } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.IO; using System.Threading; using System.Runtime.CompilerServices; namespace ConsoleApplication1 { class Program { Resource mainRes = new Resource() { data = "Main" }; Resource workerRes = new Resource() { data = "Worker" }; static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; Program p = new Program(); Thread th = new Thread(p.T2); th.Name = "Worker"; //th.IsBackground = true; th.Start(); T1(p); Console.Read(); } static void T1(Program p) { lock (p.mainRes) { Thread.Sleep(10); int i = 0; while (i < 3) { if (Monitor.TryEnter(p.workerRes)) { Console.WriteLine(p.workerRes.data); Monitor.Exit(p.workerRes); break; } else { i++; Thread.Sleep(1000); } } if (i == 3) { Console.WriteLine("{0}:deadlock", Thread.CurrentThread.Name); } } } void T2() { lock (workerRes) { Thread.Sleep(10); int i = 0; while (i < 3) { if (Monitor.TryEnter(mainRes)) { Console.WriteLine(workerRes.data); Monitor.Exit(mainRes); break; } else { i++; Thread.Sleep(1000); } } if (i == 3) { Console.WriteLine("{0}:deadlock", Thread.CurrentThread.Name); } } } } public class Resource { public string data; } }