4

我正在启动多个线程,并想知道其中任何一个何时完成。我知道以下代码:

foreach (Thread t in threads)
    t.Join();

但它只会等待所有线程在一起。那太晚了。我需要知道一个线程何时完成,即使其他线程仍在运行。我正在寻找WaitAny仅适用于线程的东西。但我无法向我正在监视的所有线程添加代码,因此不能选择使用信号或其他同步对象。

一些澄清:我正在开发一个应该记录应用程序活动的日志记录/跟踪工具。我可以在线程启动时插入日志语句,但我不能在线程外的所有可能方式上插入日志语句(多个退出点、异常等)。所以我想注册新线程,然后在它完成写入日志条目时收到通知。我可以在每个线程上异步Join,但这意味着每个受监控线程都有第二个线程,这可能看起来有点多开销。线程以各种方式使用,无论是BackgroundWorker线程Task还是池线程。从本质上讲,它是一个线程,我想知道它什么时候完成。确切的线程机制由应用程序定义,而不是日志记录解决方案。

4

5 回答 5

5

而不是 Threads 使用Tasks。它有方法WaitAny

Task.WaitAny

正如你可以在这里读到的,

  • 更高效、更可扩展地使用系统资源。
  • 比线程或工作项更多的编程控制。
于 2013-08-25T08:56:43.577 回答
1

在我看来WaitHandle.WaitAny是最好的解决方案,因为您不喜欢出于某些 xyz 原因使用它,您可以尝试这样的事情。

利用 Thread.Join(int)方法,该方法在线程终止或超时时获取millisecond timeout并返回。truefalse

List<Thread> threads = new List<Thread>();

while (!threads.Any(x=> x.Join(100)))
{

}

Join如果您知道需要多长时间,您可以更改超时。

于 2013-08-25T08:53:38.167 回答
1

我的回答是基于您对您所拥有的一切的澄清Thread.Current免责声明:IMO,你想要做的是一个黑客,因此我的想法无论如何也是一个黑客

因此,使用反射来获取所需线程的本机 Win32 句柄集。你正在寻找的Thread.GetNativeHandle方法是internal,所以你称之为thread.GetType().InvokeMember("GetNativeHandle", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, ...)。使用您选择的反射工具或框架资源来了解更多信息。获得手柄后,请继续使用以下选项之一:

  • 设置您自己的实现SynchronizationContext(从它派生)并使用SynchronizationContext.WaitHelper(waitAll: false)等待您的非托管句柄。

  • 使用原始 Win32 API,例如WaitForMultipleObjectsor CoWaitForMultipleObjects(取决于您是否需要发送消息)。

在单独的子线程或池线程上执行等待。

[已编辑]根据目标线程的执行环境,此 hack 可能不起作用,因为无法保证托管线程和非托管线程之间的一对一映射:

可以确定正在执行托管线程代码的 Windows 线程并检索其句柄。但是,为此 Windows 线程调用 SetThreadAffinityMask 函数仍然没有意义,因为托管调度程序可以在另一个 Windows 线程中继续执行托管线程

然而,这似乎只对自定义 CLR 主机有影响。此外,似乎可以使用 和 来控制托管线程关联Thread.BeginThreadAffinityThread.EndThreadAffinity

于 2013-08-25T11:04:44.647 回答
0

您可以为您的工作线程使用后台工作人员。

然后将所有RunWorkerCompleted事件挂钩到将等待它们的方法。

如果您希望将其同步到您当前正在等待加入的代码,那么问题就简化为仅将单个事件方法同步到代码中的那个位置。

更好的是,我建议在不阻塞的情况下异步执行您正在执行的操作,并在事件中执行您想要的操作。

于 2013-08-25T08:56:06.233 回答
0

你会考虑用另一个“日志”线程来包装你的线程调用吗?这样你就可以在线程运行之前和之后同步记录。

像这样的伪代码:

int threadLogger(<parms>) {
    log("starting thread");
    retcode = ActualThreadBody(<parms>);
    log("exiting thread");
    return retcode;
}

如果您有关于已启动线程的更多信息,您也可以将其记录下来。在您有多种类型的线程要启动的情况下,您也可以将线程函数作为参数,这听起来像您这样做。

于 2013-12-12T18:00:01.023 回答