等待ANY线程完成,而不是ALL
本文关键字:ALL ANY 线程 等待 | 更新日期: 2023-09-27 18:28:52
我正在启动多个线程,想知道的任何何时结束。我知道以下代码:
foreach (Thread t in threads)
t.Join();
但它只会等待所有线程在一起。太晚了。我需要知道一个线程何时结束,即使其他线程仍在运行。我正在寻找仅针对线程的等效于WaitAny
的东西。但我无法将代码添加到我正在监视的所有线程中,因此不能选择使用信号或其他同步对象。
一些澄清:我正在开发一个日志记录/跟踪工具,该工具应该记录应用程序的活动。我可以在线程启动时插入日志语句,但我不能在线程的所有可能途径(多个出口点、异常等)上插入日志语句。所以我想注册新线程,然后在它完成写入日志条目时收到通知。我可以在每个线程上异步Join
,但这意味着每个被监视的线程都有第二个线程,这似乎有点开销。线程通过各种方式使用,无论是BackgroundWorker
、Task
还是池线程。从本质上讲,这是一根线,我想知道它什么时候完成。确切的线程机制是由应用程序定义的,而不是由日志记录解决方案定义的。
使用任务而不是线程。它具有WaitAny方法。
Task.WaitAny
正如你在这里看到的,
- 更高效、更可扩展地使用系统资源
- 与线程或工作项相比,可以进行更多的编程控制
在我看来,WaitHandle.WaitAny
是最好的解决方案,因为出于某些xyz原因,您不喜欢使用它,可以尝试这样的方法。
利用Thread.Join(int)方法的优势,该方法获取millisecond timeout
,并在线程终止时返回true
,或在线程超时时返回false
。
List<Thread> threads = new List<Thread>();
while (!threads.Any(x=> x.Join(100)))
{
}
如果你知道Join
需要多长时间,你可以更改它的超时。
我的回答是基于您的澄清,即您所拥有的只是Thread.Current
免责声明:IMO,你想做的是黑客攻击,所以我的想法也是黑客攻击。
因此,使用反射来获得所需线程的一组本机Win32句柄。您正在寻找Thread.GetNativeHandle
方法,即internal
,所以您将其称为thread.GetType().InvokeMember("GetNativeHandle", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, ...)
。使用您选择的反射工具或框架来源来了解更多信息。一旦您掌握了句柄,请继续使用以下选项之一:
-
设置自己的
SynchronizationContext
实现(从中派生),并使用SynchronizationContext.WaitHelper(waitAll:false)等待非托管句柄。 -
使用原始Win32 API,如
WaitForMultipleObjects
或CoWaitForMultipleObjects
(取决于是否需要泵送消息)。
在单独的子线程或池线程上执行等待。
[EDITED]根据目标线程的执行环境,此破解可能不起作用,因为无法保证托管和非托管线程之间的一对一映射:
可以确定执行托管线程代码的Windows线程并检索其句柄。但是,为这个Windows线程调用SetThreadAffinityMask函数仍然没有意义,因为托管调度程序可以在另一个Windows线程中继续执行托管线程
然而,这可能仅适用于自定义CLR主机。此外,似乎可以使用Thread.BeginThreadAffinity
和Thread.EndThreadAffinity
来控制托管线程亲和性。
您可以使用后台工作线程。
然后将所有RunWorkerCompleted事件挂接到一个等待它们的方法。
如果您希望它与当前正在等待联接的代码同步,那么问题就简化为仅将单个事件方法同步到代码中的那个位置。
更好的是,我建议在不阻塞的情况下异步执行您正在执行的操作,并在事件中执行您想要的操作。
您会考虑用另一个"日志记录"线程包装线程调用吗?这样你就可以在&在线程运行之后。
类似这样的伪代码:
int threadLogger(<parms>) {
log("starting thread");
retcode = ActualThreadBody(<parms>);
log("exiting thread");
return retcode;
}
如果您有关于启动的线程的更多信息,您也可以将其记录下来。在有多种类型的线程要启动的情况下,您也可以将线程函数作为参数,这听起来像是您所做的。