(一)使用线程的理由
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 + "\r\n"; _msg += "当前托管线程的唯一标识:" + t.ManagedThreadId + "\r\n"; _msg += "线程名称:" + t.Name + "\r\n"; _msg += "当前线程的状态:" + t.ThreadState; MessageBox.Show("消息:\r\n" + _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); } }
三 BackgroundWorker
private void btn4_Click(object sender, EventArgs e) { progressBar.Value = 0; BackgroundWorker bw = new BackgroundWorker(); bw.WorkerReportsProgress = true;// 是否报告进度更新 // 线程执行 bw.DoWork += new DoWorkEventHandler((obj, args) => { for (int i = 0; i < 100; i++) { bw.ReportProgress(i); } }); // UI主线程显示进度 bw.ProgressChanged += (obj, progressChangedEventArgs) => { progressBar.Value = progressChangedEventArgs.ProgressPercentage; }; // 线程执行完成后的回调函数 bw.RunWorkerCompleted += (obj, runWorkerCompletedEventArgs) => { MessageBox.Show("子线程执行完成!"); }; if (!bw.IsBusy) { bw.RunWorkerAsync(); } }
三 Task(.NET 4.0以上版本)
private void btn5_Click(object sender, EventArgs e) { progressBar.Value = 0; Task<bool> t = new Task<bool>(maxValue => DoWork((int)maxValue), progressBar.Maximum); t.Start(); t.Wait(); // 任务完成后继续延续任务 Task cwt = t.ContinueWith(task => MessageBox.Show("The result is " + t.Result)); } // 线程方法 private bool DoWork(int maxValue) { for (int n = 0; n < maxValue; n++) { progressBar.BeginInvoke(new EventHandler((sender, e) => { progressBar.Value = n; }), null); } return true; }
四 异步委托
public delegate string MyDelegate(object arg); private void btn6_Click(object sender, EventArgs e) { MyDelegate myDelegate = new MyDelegate(DoWork3); IAsyncResult result = myDelegate.BeginInvoke(100, DoWork2Callback, "回调函数参数"); // 异步执行完成 string resultStr = myDelegate.EndInvoke(result); } // 线程函数 private string DoWork3(object arg) { for (int n = 0; n < (int)arg; n++) { progressBar.BeginInvoke(new EventHandler((sender, e) => { progressBar.Value = n; }), null); } return "Finished"; } // 异步回调函数 private void DoWork2Callback(IAsyncResult arg) { MessageBox.Show(arg.AsyncState.ToString()); }
五 附 跨线程访问UI之 SynchronizationContext (同步上下文)
private void btn2_Click(object sender, EventArgs e) { SynchronizationContext context = SynchronizationContext.Current; new Thread(() => { for (int i = 0; i < 100; i++) { // Send方法是发送一个异步请求消息 //context.Send((s) => //{ // progressBar.Value = i; //}, null); // Post方法是发送一个同步请求消息 context.Post((s) => { progressBar.Value = i; }, null); } }).Start(); }
六 Task+事件+控制(暂停、继续):
using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace TaskTest { public partial class Form1 : Form { public event EventHandler<MyEventArgs> MyNotify; private delegate void DelegateSetProgress(MyEventArgs e); CancellationTokenSource tokenSource; CancellationToken token; ManualResetEvent reset; public Form1() { InitializeComponent(); MyNotify += new EventHandler<MyEventArgs>(GetProgress); } private void SetProgress(MyEventArgs e) { if(progressBar1.InvokeRequired) { Invoke(new DelegateSetProgress(SetProgress), e); } else { progressBar1.Value = e.value; txtMessage.AppendText(e.text); } } private void btnStart_Click(object sender, EventArgs e) { tokenSource = new CancellationTokenSource(); token = tokenSource.Token; reset = new ManualResetEvent(true); // 初始化为true时执行WaitOne不阻塞 MyNotify(null, new MyEventArgs() { value = 0, text = $"程序已经启动\n" }); Task.Run(() => { for (int i = 1; i <= 100; i++) { if (token.IsCancellationRequested) return; reset.WaitOne(); MyNotify(null, new MyEventArgs() { value = i, text = $"进度为:{i}/100 \n" }); Thread.Sleep(100); } },token); } private void GetProgress(object sender,EventArgs e) { SetProgress(e as MyEventArgs); } private void btnPause_Click(object sender, EventArgs e) { btnPause.Text = btnPause.Text == "暂停" ? "继续" : "暂停"; if (btnPause.Text == "暂停") reset.Set(); else reset.Reset(); } private void btnStop_Click(object sender, EventArgs e) { tokenSource.Cancel(); } } public class MyEventArgs : EventArgs { public int value = 0; public string text = string.Empty; } }