Windows原理与应用(三)——线程间通信与同步
线程
- 进程是计算机分配资源的单位,线程是运行调度单位
- 进程中的线程也具有线程控制块,包含内容有所属进程ID,创建和退出时间,线程启动地址等
线程创建过程
- 在进程的地址空间中为线程创建用户态堆栈。
- 初始化线程硬件上下文。
- 创建线程对象。
- 通知内核系统为线程运行准备。
- 新创建线程handle和线程ID值返回到调用者。
- 线程进入调度准备执行。
线程的生命周期
启动→执行→暂停→终止
工作线程的结束
- 线程正常结束
- 线程非正常结束,被KILL
- 控制线程正常终止的方法
线程非正常结束的后果
- 内存无法回收 - 内存泄漏
- 文件缓冲没写入 - 文件被破坏
- 文件句柄未回收 - 被占用
- 共享资源的占用(网络端口,管道,DLL)
线程的创建与启动 - C#
多线程与线程跨域访问
Windows中每个进程都至少有一个线程,称为主线程,对于通常的窗体应用,主线程与窗体界面绑定
如果有耗时很长的处理函数在主线程中运行,会导致界面停滞,失去响应,所以需要把工作交给工作线程执行,执行结束后将结果通知给界面线程
处于安全性考虑,不同线程之间不可以相互调用对方的资源,即不允许直接跨域访问,需要使用委托回调的方式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Work { public delegate void update_listbox(string new_info, string listname); update_listbox callback; string list_name; public Work(update_listbox callback, string list_name) { this.callback = callback; this.list_name = list_name; } }
private void update_output(string new_info, string list_name) { Dispatcher.Invoke(() => { ListBox lb = FindName(list_name) as ListBox; ListBoxItem li = new ListBoxItem(); li.Content = new_info; lb.Items.Add(li); });
Work wk = new Work(update_output, "listname");
|
异步线程
Thread
- 不含参数的构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using System.Threading;
void workThread(){ }
ThreadStart s = new ThreadStart(workThread);
Thread thread1 = new Thread(s); thread.Name = "设定一个名称"; thread.IsBackground = true;
thread1.Start();
thread1.Start(paraObject);
|
1 2 3 4 5 6 7 8 9 10
|
Thread thread1 = new Thread(() => Console.WriteLine("我是通过Lambda表达式创建的委托")); thread1.Start();
Thread thread2 = new Thread(delegate() { Console.WriteLine("我是通过匿名委托创建的线程"); }); thread2.Start();
|
- 含参数的构造
1 2 3 4 5 6 7 8 9 10 11
|
Thread thread = new Thread(new ParameterizedThreadStart(Thread1));
thread.Start("这是一个有参数的委托");
void Thread1(object obj){ Console.WriteLine(obj); }
|
- Thread默认只有这两种构造方式,如果要传入多个参数,需要用类来实现
操作Thread的常用函数
方法名称 |
说明 |
Abort() |
终止本线程 |
GetDomain() |
返回当前线程正在其中运行的当前域 |
GetDomainId() |
返回当前线程正在其中运行的当前域Id |
Interrupt() |
中断处于Wait Sleep Join线程状态的线程 |
Join() |
阻塞调用线程,直到某个线程终止时为止 |
Resume() |
继续运行已挂起的线程 |
Start() |
执行本线程 |
Suspend() |
挂起当前线程,如果当前线程已属于挂起状态则此不起作用 |
Sleep() |
把正在运行的线程挂起一段时间 |
Thread的常用属性
属性名 |
说明 |
IsAlive |
获取一个值,表示当前线程的执行状态 |
Name |
获取或设置Thread的名称 |
ThreadState |
获取一个值,表示当前线程的状态 |
IsBackground |
获取一个值,指示当前线程是否为后台线程 |
Task
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| using System.Threading.Tasks;
Task t = new Task(() => {
}); t.Start();
void yourTaskFun(){ } Task t2 = new Task(yourTaskFun); t.Start();
|
1 2 3 4 5 6
| Task<int> t = new Task<int>(() => { return 114; }); t.Start();
|
前台线程与后台线程
- 前台线程
- 所有前台线程都结束,应用程序才可以结束;默认创建的线程都是前台线程
- 后台线程
- 只要所有前台线程结束,后台线程自动结束
- 通过Thread.IsBackground设置后台线程
- 必须在Start之前设置后台线程
线程同步
互斥量
1 2 3 4 5 6
| Mutex mut_operate = new Mutex();
mut_operate.WaitOne();
mut_operate.ReleaseMutex();
|
1 2 3 4 5 6 7
| Semaphore semaphore = new Semaphore(usable_num , max_num + 1);
semaphore.WaitOne();
semaphore.Release();
|
ManualResetEvent与AutoResetEvent
1 2 3 4 5 6 7
| stop_signal = new ManualResetEvent(false);
while (!stop_signal.WaitOne(1)){ }
|
1 2 3 4 5
| stop_signal.Set();
stop_signal.Reset();
|
与ManualResetEvent效果相同,只不过WaitOne()有效并被读取一次后会立即自动置为False,不需要手动Reset,这意味着它一次只能通知一个线程
线程缺点
信号量实例——生产者消费者协同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| private void produce_one() { string msg; while (!stop_signal.WaitOne(1)) {
mut_operate.WaitOne(); if (usable_num == max_num) { mut_operate.ReleaseMutex(); continue; } mut_operate.ReleaseMutex(); msg = Thread.CurrentThread.Name + "即将开始工作!\n\t当前资源总量为:" + usable_num + ",资源上限为:" + max_num + "\n\t正在工作..."; callback(msg, listname); Random rand = new Random(); Thread.Sleep(rand.Next(0, Produce_time_upper_bound));
while (true) { mut_operate.WaitOne(); if (usable_num == max_num) { mut_operate.ReleaseMutex(); continue; } else { break; } } usable_num++;
msg = Thread.CurrentThread.Name + "生产完成,当前资源总量为:" + usable_num + "\n"; callback(msg, listname); callback_2.Invoke(usable_num); semaphore.Release(); mut_operate.ReleaseMutex();
} msg = Thread.CurrentThread.Name + "结束"; callback(msg, listname); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private void consume_one() { string msg; while (!stop_signal.WaitOne(1)) { semaphore.WaitOne();
msg = Thread.CurrentThread.Name + "即将开始工作!\n\t当前资源总量为:" + usable_num + ",资源上限为:" + max_num + "\n\t正在工作..."; callback(msg, listname); Random rand = new Random(); Thread.Sleep(rand.Next(0, Consume_time_upper_bound)); mut_operate.WaitOne(); usable_num--; mut_operate.ReleaseMutex(); msg = Thread.CurrentThread.Name + "消费完成,当前资源总量为:" + usable_num + "\n"; callback(msg, listname); callback_2.Invoke(usable_num); } msg = Thread.CurrentThread.Name + "结束"; callback(msg, listname); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public void start_simulation() { stop_signal = new ManualResetEvent(false);
int i = 0; while (i < producer_num) { Thread thread = new Thread(new ThreadStart(produce_one)); thread.Name = "生产者" + i + "号"; thread.IsBackground = true; thread.Start(); i++; } i = 0; while (i < consumer_num) { Thread thread = new Thread(new ThreadStart(consume_one)); thread.Name = "消费者" + i + "号"; thread.IsBackground = true; thread.Start(); i++; } }
|
1 2 3 4 5 6 7
| semaphore = new Semaphore(usable_num , max_num);
|