彼年豆蔻,谁许谁地老天荒。
在多线程环境下,我们可能会需要等待开辟的线程执行完后,再去执行某个方法,例如输出并行计算结果等。
但是在多线程下,线程的执行是不阻塞主线程的,这点其实也是多线程的优势,提高代码执行效率,不必相互等待可以并行执行
例如如下代码:
private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < 3; i++) { Task task = new Task((obj) => { System.Threading.Thread.Sleep(500); MessageBox.Show(obj + ""); },i); task.Start(); } MessageBox.Show("线程执行完毕了"); }
当点击按钮开启线程后,先弹出来的不是开启的线程弹窗,而是主线程的线程弹窗。
现在我们来通过某些方法,实现可以在多线程执行完毕后在执行自己的逻辑
几种常用的处理方法
1:使用Task.WaitAll (会阻塞主线程)
可以使用Task.WaitAll让主线程一直等待开辟的线程,直到所有子线程都执行完后再执行主线程逻辑
private void button1_Click(object sender, EventArgs e) { Task[] tasks = new Task[3]; for (int i = 0; i < 3; i++) { Task task = new Task((obj) => { System.Threading.Thread.Sleep(500); MessageBox.Show(obj + ""); },i); tasks[i] = task; task.Start(); } Task.WaitAll(tasks); MessageBox.Show("线程执行完毕了"); }
Task.WaitAll 是一个等待的过程,一旦全部执行完毕了,就继续往下执行,这里是阻塞的
2:使用Task.WhenAll(不会阻塞主线程)
Task.WhenAll和Task.WaitAll类似都可以做到线程执行后在向下执行,但是wait是等待也就是会阻塞主线程,而when表示当的意思,就是当子线程都执行完后在执行一个回调函数。可以把子线程执行完毕后想执行的代码放入该回调函数里边。
private void button1_Click(object sender, EventArgs e) { Task[] tasks = new Task[3]; for (int i = 0; i < 3; i++) { Task task = new Task((obj) => { System.Threading.Thread.Sleep(2000); MessageBox.Show(obj + ""); }, i); tasks[i] = task; task.Start(); } Task.WhenAll(tasks).ContinueWith(a => { MessageBox.Show("线程执行完毕了"); }); MessageBox.Show("主线程被执行了"); }
3:使用Parallel.Invoke或者 Parallel.For(会阻塞主线程)
Parallel.Invoke或者 Parallel.For可以很方便的开辟多线程并行执行,自动会阻塞多线程。所以他后面写的代码会
自动等待子线程执行完毕后再执行
private void button2_Click(object sender, EventArgs e) { //开辟子线程,并行执行 Parallel.Invoke(() => { System.Threading.Thread.Sleep(1500); MessageBox.Show("线程A"); }, () => { System.Threading.Thread.Sleep(1500); MessageBox.Show("线程B"); }); //开辟子线程,并行执行 Parallel.For(0, 3, (a) => { System.Threading.Thread.Sleep(1500); MessageBox.Show("线程" + a); }); MessageBox.Show("线程执行完毕"); }
4:自己另开线程,监控线程状态(不会阻塞主线程)
其实我们可以单独另外开辟一个新的线程了,用来监控所有子线程的运行状态,当这些被监控的线程都执行完毕后即可执行自己的逻辑,当然也可以封装一个回调函数用于方便调用。
线程的状态有很多:例如Created(已经创建),Running(运行中),RanToCompletion(运行完毕)等
我们就可以利用这些线程状态来做一些自定义操作。例如这里的等待线程执行完毕等。
static void Main(string[] args) { Task task = new Task(() => { System.Threading.Thread.Sleep(2000); Console.WriteLine("线程执行完了"); }); task.Start(); Console.WriteLine("-------开始监听单线程执行-------"); //一个单独的线程 监控其他线程的状态 Task monitor_task = new Task(() => { while (true) { if (task.Status == TaskStatus.RanToCompletion) { Console.WriteLine("监控到线程执行完了"); break; } } }); monitor_task.Start(); Console.ReadLine(); }
监控多个线程其实也是一样,监控的时候判断执行完成线程的个数就行了
static void Main(string[] args) { Task[] tasklist = new Task[10]; Dictionary<object, object> dic = new Dictionary<object, object>(); for (int i = 0; i < 10; i++) { Task task = new Task((a) => { System.Threading.Thread.Sleep(1000); dic.Add(a, a); }, i); tasklist[i] = task; task.Start(); } //用户监控其他线程的状态 Task monitor_task = new Task(() => { //监控线程的个数 int i = 0; while (true) { i = 0; foreach (Task item in tasklist) { if (item.Status == TaskStatus.RanToCompletion) { i++; } } if (i == 10) { Console.WriteLine("所有线程执行完毕" + dic.Count); break; } } }); monitor_task.Start(); Console.ReadLine(); }
欢迎加群讨论技术,群:677373950(满了,可以加,但通过不了),2群:656732739