草稿箱告急,半成品也先发一下吧。。。
1. 线程
线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。
下面列出了线程生命周期中的各种状态:
- 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
- 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
- 不可运行状态:下面的几种情况下线程是不可运行的:
- 已经调用 Sleep 方法
- 已经调用 Wait 方法
- 通过 I/O 操作阻塞
- 死亡状态:当线程已完成执行或已中止时的状况。
在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。当程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。可以使用 Thread 类的 CurrentThread 属性访问线程。
示例:
using System; using System.Threading; namespace MultithreadingApplication { class MainThreadProgram { static void Main(string[] args) { Thread th = Thread.CurrentThread; th.Name = "MainThread"; Console.WriteLine("This is {0}", th.Name); Console.ReadKey(); } } }
输出
This is MainThread
2. Lock
lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。
在C# lock关键字定义如下:
lock(expression) statement_block
其中expression代表你希望跟踪的对象,通常是对象引用。如果你想保护一个类的实例,一般地,你可以使用this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。
示例:
using System; using System.Threading; namespace ThreadDemo { class Program { private static object myLockObject = new object(); public static void fun() { lock (myLockObject) { int i = 0; while (i < 1000) { Console.WriteLine("In {0},i={1}", Thread.CurrentThread.Name,i); i++; } } } static void Main(string[] args) { Thread.CurrentThread.Name = "MainThread"; Thread childthread = new Thread(new ThreadStart(fun)); childthread.Name = "ChildThread"; Console.WriteLine("In Main: Creating the Child thread"); childthread.Start(); fun(); } } }
结果:在主线程执行完再在线程执行,不然一会在主线程一会在子线程。
In Main: Creating the Child thread In MainThread,i=0 ... In MainThread,i=999 In ChildThread,i=0 ... In ChildThread,i=999
3. WaitHandle
|——EventWaitHandle 事件构造。
|——AutoResetEvent
|——ManualResetEvent
|——Semaphore 信号量构造。
|——Mutex 互斥体构造。
3.1 EventWaitHandle
EventWaitHandle是AutoResetEvent和ManualResetEvent的基类,从EventWaitHandle可以创建两者。EventWaitHandle 类用于在异步操作时控制线程间的同步,即控制一个或多个线程继行或者等待其他线程完成。
EventWaitHandle(bool initialState, EventResetMode mode);
initialState设置初始状态,如果为true,则WaitOne()在调用Reset()方法前不会阻塞线程,先调用Reset()再调用WaitOne()会阻塞WaitOne()所在线程;如果设置为false,则WaitOne()会阻塞,直到调用Set()。
mode设置为EventResetMode.ManualReset,在调用Set()时所有WaitOne()阻塞线程都会继续执行;设置为EventResetMode.AutoReset则每次调用Set()会使一个WaitOne()阻塞的线程继续执行,一般按照阻塞先后顺序执行。
- AutoResetEvent
- ManualResetEvent
当A线程的活动结束后,B线程才能开始的情况下,首先,调用Reset 以将 ManualResetEvent 置于非终止状态(false),此线程(A)可被视为控制 ManualResetEvent。在B线程调用ManualResetEvent 上的WaitOne将线程阻止。当控制线程(A)完成活动时,它调用Set以发出等待线程可以继续进行的信号。ManualResetEvent 将保持终止状态(true),直到它被手动重置(Reset)。
示例:主线程进行i++计算,子线程打印。如果主线程的计算很慢,阻塞子线程打印,可以避免重复打印。
using System; using System.Threading; namespace ThreadDemo { class Demo { static ManualResetEvent printevent, printexit; static int i = 0; public static void count() { Thread.Sleep(10); i++; Console.WriteLine("In thread{0}, i++", Thread.CurrentThread.ManagedThreadId, i); printevent.Set();//将事件状态设置为终止状态,允许子线程继续 } public static void print() { while (true) { printevent.WaitOne(); Thread.Sleep(50); Console.WriteLine("In thread{0}, i = {1}", Thread.CurrentThread.ManagedThreadId, i); printevent.Reset();//将事件状态设置为非终止状态,导致线程阻塞 } } static void Main(string[] args) { printevent = new ManualResetEvent(false); Thread childThread = new Thread(print); childThread.Start(); while (i < 1000) { count(); } } } }
示例二:如果是AutoResetEvent,可以省略manualWaitHandler.Reset();如果是ManualResetEvent,不加Reset()的话,WaitOne()无效,直接往下运行了。
ManualResetEvent manualWaitHandler = new ManualResetEvent(false);//false 即非终止,未触发。 new Thread(() => { manualWaitHandler.WaitOne(); //阻塞当前线程对象,等待信号。 Console.WriteLine("接收到信号,开始处理。"); manualWaitHandler.Reset(); //手动 设置事件对象状态为非终止状态,false。
manualWaitHandler.WaitOne(); //这里直接阻塞等待无效,因为事件对象还是true,必须手动调reset。 Console.WriteLine("第二次接收到信号,开始处理。"); }).Start(); new Thread(() => { Thread.Sleep(2000); Console.WriteLine("发信号"); manualWaitHandler.Set(); //向事件对象发送ok信号。。 Thread.Sleep(2000); Console.WriteLine("第二次发信号"); manualWaitHandler.Set(); }).Start(); Console.ReadLine();