线程的学习与使用
-
一、线程的简单创建
可以把要执行的方法当做参数传递给线程,还有一种就是通过Lambda表达式匿名函数去直接创建。
static void Test() { ///直接把方法传递给线程 Thread thread = new Thread(Test); thread.Start(); ///匿名函数创建线程 Thread th2 = new Thread(() => { Console.WriteLine("匿名创建"); }); th2.Start(); Console.ReadKey(); Console.WriteLine("方法"); }
-
线程的一些简单操作
nterrupt()。这个方法作用是唤醒沉睡的线程。当线程沉睡的时候,调用Interrupt()方法,这会把当前沉睡的线程唤醒,并抛出ThreadInterruptedException。
//唤醒线程 Thread thread = new Thread(() => { Console.WriteLine("线程开始啦"); Console.WriteLine("睡着啦"); try { Thread.Sleep(50000); } catch (ThreadInterruptedException ex) { Console.WriteLine("被叫醒啦"); } Console.WriteLine("醒啦"); }); thread.Start(); Console.WriteLine("按任意键唤醒线程"); Console.ReadKey(); thread.Interrupt(); Console.ReadKey();
三、线程的一些小细节
Thread.Sleep();//休眠线程,即让当前的线程休眠指定的时间。
Thread.Abort ();//终止线程,即抛出一个ThreadAbortException;
线程的参数化:
当执行下面的语句我们想要输出的值为5但实际输出的却为6,因为在线程还未执行完成的时候又重新给i赋值为6,所以当线程执行的时候输出i的值就为6了。若想让线程输出指定的值的话,就得为该线程传递参数进去,给线程指定
默认的参数。
线程创建的时候默认的为非后台线程。当所有的非后台线程全部执行完后,进程才会关闭,否则看起来主线程关闭了,但是该进程却并没有关闭,依旧在后台运行着等待着其余非后台线程的执行。
把线程设置为后台线程就不会出现上述的情况了,把线程设置为"后台线程"后,所有"非后台线程"执行结束后程序就会退出,不会去等后台程序是否执行完。设置后台线程:thread.IsBackground
线程的优先级:
thread.Priority = ThreadPriority.Normal;//线程创建时的默认级别
thread.Priority = ThreadPriority.Highest;//最高优先级
thread.Priority = ThreadPriority.Lowest;//最低优先级
thread.Priority = ThreadPriority.BelowNormal;//在Normal之后Lowest之前
thread.Priority = ThreadPriority.AboveNormal;//在Highest之后在Normal
四、线程同步
下面程序按理来说输出的值应该为10000才对,但是输出的却不是10000.原因很简单就是因为当thread线程在操作count的时候,thread2也在操作count。当thread为count赋值之后,thread2重新再为count赋值就把count的值重新又给初始化
了,所以count的值输出的就不为10000。
线程同步就是解决多个线程同时操作一个资源的问题。下面代码就是简单的实现线程同步。使用lock去解决多线程的问题,lock 是 C#中的关键字,他要锁定一个资源,lock 的特点是:同时只能有一个线程进入 lock 的对象的范围,
其他 lock 的线程就要等。就是多个线程同一时候只能有一个线程去执行被标记锁的对象。
int count = 0; Thread thread = new Thread(() => { lock (Lock)//添加锁 设置线程锁的对象同一时间只能有一个线程去操作它 { for (int i = 0; i < 5000; i++) { count++; Thread.Sleep(1); } } }); Thread thread2 = new Thread(() => { lock (Lock)//添加锁 { for (int j = 0; j < 5000; j++) { count++; Thread.Sleep(1); } } }); thread2.Start(); thread.Start(); while (thread.IsAlive) { } while (thread2.IsAlive) { }//等待线程结束 Console.WriteLine(count);
五、Monitor
lock关键字相当于对Monitor的调用。Monitor类的作用就是把一个对象给锁起来,若有线程在用的话就等待对方释放解锁,然后自己用的时候再把这个对象给锁起来。Monitor的三个最常用的方法就是。
Monitor.Enter();//为指定对象加上锁
Monitor.Exit(Lock);//释放锁,释放对象,解锁。
Monitor.TryEnter();这个方法放回的是一个bool类型,若指定的对象被锁定之后,它不会去等待释放,而是返回false。下面就是对Monitor的简单应用。
public static Object Lock = new object();//设置线程锁的对象同一时间只能有一个线程去操作它 static void Main(string[] args) { int count = 0; Thread thread = new Thread( () => { for (int i = 0; i < 100; i++) { try { Monitor.Enter(Lock);//加上锁 count++; Console.WriteLine("开始借钱"); Thread.Sleep(1000); Console.WriteLine("借了" + count); } finally { Monitor.Exit(Lock);//释放锁 } } } ); Thread thread2 = new Thread( () => { for (int j = 0; j < 100; j++) { try { Monitor.Enter(Lock);//加上锁 count++; Console.WriteLine(DateTime.Now.ToString() + "--开始还钱"); Thread.Sleep(1000); Console.WriteLine(DateTime.Now.ToString() + "还了" + count); } finally { Monitor.Exit(Lock);//释放锁 } } } ); thread.Start(); thread2.Start(); thread.Join(); thread2.Join(); }
六、单例模式练习
1、饿汉模式
public class GetOne { //单例模式之饿汉模式 private static GetOne test = new GetOne(); private GetOne()//构造函数私有化 { } public static GetOne GetOnly() { return test; } }
2、懒汉模式加上线程的同步
public class GetOne { private static Object Lock = new object();//创建锁对象 //单例模式之懒汉式 private static GetOne test = null; private GetOne()//构造函数私有化 { } public static GetOne GetOnly() { if (test == null)//判断GetOne是否为null。多加上这个判断是为了减少上锁的次数 { lock (Lock)//若为null就加上锁去创建,防止多个线程同时创建 { if (test == null) { test = new GetOne(); } } } return test; } }
七、线程池
ThreadPool.QueueUserWorkItem();//里面传入委托。还可以在后面添加一个Object的参数,这个参数的意义就是实现线程的参数化。
WaitHandle 线程间的通讯。他有两个子类是经常用到的。
ManualResetEvent:这个就相当于与是手动去操作。把线程间的通讯表示成开门关门的话。当创建 ManualResetEvent man=new ManualResetEvent(false)
;这个类的时候,false就代表初始化的时候就已经关上门了。当线程中调用 man.WaitOne();就带表了这个线程一直在等待(WaitOne()可以添加参数,表示等待的时间),
等待着开门。当线程调用man.Set();的时候,该线程就开门了,线程就会继续往下执行。man.Reset();手动关门
第二个子类是AutoResetEvent。这个就是在执行WaitOne()线程等待的时候,若发现开门之后,当执行完线程后会自动把门给关闭。