[C#] 多线程总结(结合进度条)
(一)使用线程的理由
1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。
2、可以使用线程来简化编码。
3、可以使用线程来实现并发执行。
(二)基本知识
1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源。
2、前台线程和后台线程:通过Thread类新建线程默认为前台线程。当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。
3、挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程序的执行情况不可预知,所以使用挂起和唤醒容易发生死锁的情况,在实际应用中应该尽量少用。
4、阻塞线程:Join,阻塞调用线程,直到该线程终止。
5、终止线程:Abort:抛出 ThreadAbortException 异常让线程终止,终止后的线程不可唤醒。Interrupt:抛出 ThreadInterruptException 异常让线程终止,通过捕获异常可以继续执行。
6、线程优先级:AboveNormal BelowNormal Highest Lowest Normal,默认为Normal。
(三) 线程生命周期
- 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
- 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
-
不可运行状态:死亡状态:当线程已完成执行或已中止时的状况。
- 已经调用 Sleep 方法
- 已经调用 Wait 方法
- 通过 I/O 操作阻塞
(四) Thread 常用方法:
- public void Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。
- public void Join() 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。
- public void Start() 开始一个线程
- public static void Sleep(int millisecondsTimeout) 让线程暂停一段时间
一 普通线程
分为两种,一种是不需要给子线程传参数,Thread t = new Thread(new ThreadStart(void () target)); 另一种是要给子线程传一个参数,Thread t = new Thread(new ParameterizedThreadStart(void (object) target));
// 普通线程 private void btn1_Click(object sender, EventArgs e) { progressBar.Value = 0; Thread tt = new Thread(new ThreadStart(DoWork1)); tt.Name = "不带参数普通线程"; tt.Start(); Thread t = new Thread(new ParameterizedThreadStart(DoWork2)); t.Name = "带参数普通线程"; t.IsBackground = true; t.Start(100); _msg += "当前线程的执行状态:" + t.IsAlive + " "; _msg += "当前托管线程的唯一标识:" + t.ManagedThreadId + " "; _msg += "线程名称:" + t.Name + " "; _msg += "当前线程的状态:" + t.ThreadState; MessageBox.Show("消息: " + _msg, "提示", MessageBoxButtons.OK); } // 线程方法 private void DoWork1() { for (int i = 0; i < 100; i++) { // 跨线程访问 UI,BeginInvoke 采用异步委托 progressBar.BeginInvoke(new EventHandler((sender, e) => { progressBar.Value = i; }), null); } } // 线程方法 private void DoWork2(object obj) { for (int i = 0; i < (int)obj; i++) { progressBar.BeginInvoke(new EventHandler((sender, e) => { progressBar.Value = i; }), null); } }
二 线程池
public static bool QueueUserWorkItem(WaitCallback);
public static bool QueueUserWorkItem(WaitCallback, object);
线程池默认为后台线程(IsBackground)
private void btn3_Click(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(DoWork2, 100); // 或者 ThreadPool.QueueUserWorkItem((s) => { int minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, maxCompletionPortThreads; ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); MessageBox.Show(String.Format("WorkerThreads = {0} ~ {1}, CompletionPortThreads = {2} ~ {3}", minWorkerThreads, maxWorkerThreads, minCompletionPortThreads, maxCompletionPortThreads)); DoWork2(100); }); } // 线程方法 private void DoWork2(object obj) { for (int i = 0; i < (int)obj; i++) { // Thread.Sleep(50); progressBar.BeginInvoke(new EventHandler((sender, e) => { progressBar.Value = i; }), null); } }