VB.NET多线程中如何判断线程状态及等待全部子线程完成
在VB.NET里判断线程状态,主要看你用的是传统的Thread类还是更现代的Task并行库,两种方式各有特点:
用Thread类的IsAlive属性
每个Thread实例都自带IsAlive属性,逻辑很直白:
- 线程没启动、已经跑完了,它返回
False - 线程正在跑(哪怕是被阻塞在等待状态),它返回
True
给你个简单的示例:
Dim workerThread As New Thread(AddressOf DoWork) workerThread.Start() ' 检查当前状态 If workerThread.IsAlive Then Console.WriteLine("线程还在干活呢") Else Console.WriteLine("线程要么没启动,要么已经干完了") End If Sub DoWork() ' 模拟耗时操作 Thread.Sleep(2000) End Sub
⚠️ 提醒一句:IsAlive只能告诉你当前瞬间的状态,线程可能在你检查完的下一秒就切换状态了,所以如果要基于这个做业务逻辑判断,最好结合同步锁或者其他同步机制,避免出现竞态条件。
用Task的Status属性(更推荐,现代.NET的首选)
如果你用的是.NET 4.0及以后的版本,更建议用Task并行库,它的状态更精细:
TaskStatus.Created:任务建好了但还没启动TaskStatus.Running:任务正在执行TaskStatus.RanToCompletion:任务成功跑完了TaskStatus.Faulted:任务执行出错了TaskStatus.Canceled:任务被取消了
示例代码:
Dim workerTask As Task = Task.Run(AddressOf DoWork) ' 检查状态 Select Case workerTask.Status Case TaskStatus.Running Console.WriteLine("任务正在执行") Case TaskStatus.RanToCompletion Console.WriteLine("任务已经圆满完成") Case Else Console.WriteLine($"任务当前状态:{workerTask.Status}") End Select
这种动态生成子线程的场景确实麻烦,没法提前把所有子线程的引用都攥在手里。我之前处理过类似需求,给你几个靠谱的解决方案:
方案1:用CountdownEvent同步(适合传统Thread场景)
CountdownEvent是专门干这种事的同步原语——你先设置一个初始计数,每完成一个线程就调用Signal()把计数减1,等计数降到0,等待的线程就会被唤醒。
步骤很清晰:
- 父线程里创建
CountdownEvent,初始计数设为0(因为子线程是动态生成的,后续每加一个子线程就先把计数加1) - 每个子线程执行完(哪怕出错),一定要调用
Signal(),别让父线程一直等 - 父线程调用
Wait()等着,等所有子线程都Signal了,就执行你的目标方法
示例代码:
Sub ParentWork() Dim countdown As New CountdownEvent(0) ' 初始计数0,动态增减 ' 比如动态生成3个子线程 For i As Integer = 1 To 3 countdown.AddCount() ' 先加计数,再启动线程 Dim threadIndex = i ' 捕获循环变量,避免闭包陷阱 Dim childThread As New Thread(Sub() Try Console.WriteLine($"子线程{threadIndex}开始干活") Thread.Sleep(1500) ' 模拟耗时工作 Finally ' 不管成功失败,都要Signal,防止死等 countdown.Signal() End Try End Sub) childThread.Start() Next ' 等所有子线程都干完 countdown.Wait() Console.WriteLine("所有子线程都搞定了,开始调用目标方法") MyTargetMethod() ' 用完记得释放资源 countdown.Dispose() End Sub Sub MyTargetMethod() Console.WriteLine("目标方法执行中...") End Sub
方案2:用Task.WhenAll(推荐,现代.NET首选)
如果把子线程都换成Task,处理起来就简单多了。Task.WhenAll可以等待一组Task全部完成,还支持Await,不会阻塞主线程(尤其是UI线程场景非常友好)。
示例代码:
Async Sub ParentTaskWork() Dim childTasks As New List(Of Task)() ' 动态生成子Task For i As Integer = 1 To 3 Dim task = Task.Run(Sub() Console.WriteLine($"子Task{i}正在执行") Thread.Sleep(1500) End Sub) childTasks.Add(task) Next ' 等待所有子Task完成 Await Task.WhenAll(childTasks) Console.WriteLine("所有子Task都完成了,调用目标方法") MyTargetMethod() End Sub
如果你的父方法不能用异步(比如是同步的控制台程序入口),也可以用Task.WhenAll(childTasks).Wait(),但要注意在UI线程里别这么干,容易死锁,UI线程优先用Await。
方案3:用ManualResetEvent数组(适合固定数量子线程的场景)
如果子线程数量是固定的,也可以用ManualResetEvent数组,每个子线程对应一个事件,干完活就把事件设为信号状态,然后用WaitHandle.WaitAll()等所有事件都触发。
示例代码:
Sub ParentThreadWithResetEvents() Dim threadCount = 3 Dim resetEvents(threadCount - 1) As ManualResetEvent For i = 0 To threadCount - 1 resetEvents(i) = New ManualResetEvent(False) Dim index = i ' 捕获循环变量 Dim childThread As New Thread(Sub() Console.WriteLine($"子线程{index+1}开始工作") Thread.Sleep(1500) ' 干完活设置事件 resetEvents(index).Set() End Sub) childThread.Start() Next ' 等待所有事件触发 WaitHandle.WaitAll(resetEvents) Console.WriteLine("所有子线程完成,调用目标方法") MyTargetMethod() ' 释放资源 For Each evt In resetEvents evt.Dispose() Next End Sub
内容的提问来源于stack exchange,提问作者Khurram Zulfiqar

