为啥我要启动一个“暂停”的线程?

Posted

技术标签:

【中文标题】为啥我要启动一个“暂停”的线程?【英文标题】:Why would I want to start a thread "suspended"?为什么我要启动一个“暂停”的线程? 【发布时间】:2010-07-01 10:17:20 【问题描述】:

Windows 和 Solaris 线程 API 都允许在“暂停”状态下创建线程。线程仅在稍后“恢复”时才真正开始。我已经习惯了没有这个概念的 POSIX 线程,我正在努力理解它的动机。谁能建议为什么创建一个“暂停”线程会很有用?

这是一个简单的说明性示例。 WinAPI 允许我这样做:

t = CreateThread(NULL,0,func,NULL,CREATE_SUSPENDED,NULL);
// A. Thread not running, so do... something here?
ResumeThread(t);
// B. Thread running, so do something else.

(更简单的)POSIX 等效项似乎是:

// A. Thread not running, so do... something here?
pthread_create(&t,NULL,func,NULL);
// B. Thread running, so do something else.

有没有人能够在 A 点(在 CreateThread 和 ResumeThread 之间)做一些在 POSIX 上很难做到的实际例子?

【问题讨论】:

哦,顺便说一句,我知道直接使用 CreateThread 实际上并不是您应该在 C/C++ 中执行的操作 - 它不会让 C-Runtime 有机会初始化各种内务处理。应该使用 _beginthreadex 代替(或者我读到了。) 在 POSIX 上实现这样的机制并不难。您将不得不使用简单的 pthread_cond_wait @Ragster - 感谢您的提示。 _beginthreadex() 确实是在 Windows 上启动线程的正确方法。 【参考方案1】:
    预先分配资源,然后几乎立即启动线程。 您有一种重用线程的机制(恢复它),但实际上您没有要重用的线程,您必须创建一个。

【讨论】:

这两个答案都暗示创建线程的成本很高。我在 Google 上闲逛了一下,发现 Windows 和 Linux 都需要大约 0.2 毫秒来创建一个线程。这确实是相当昂贵的。 重复多次可能会造成严重损失。 还要考虑线程使用的系统资源——(虚拟)内存、句柄、上下文切换——所有线程池的好理由。【参考方案2】:

在许多情况下创建处于挂起状态的线程可能很有用(我发现) - 您可能希望在允许线程开始使用您正在使用的资源之前获取线程的句柄并设置它的一些属性设置它。

启动被暂停比启动它然后暂停它安全得多 - 你不知道它已经走了多远或它在做什么。

另一个例子可能是当你想使用线程池时——你先创建必要的线程,挂起,然后当请求进来时,选择一个线程,设置任务的线程信息,然后然后将其设置为可调度。

我敢说没有 CREATE_SUSPENDED 是有办法的,但它肯定有它的用途。

如果您想了解更多细节,可以参考“Windows via C/C++”(Richter/Nasarre) 中的一些使用示例!

【讨论】:

【参考方案3】:

CreateThread 中有一个隐含的竞争条件:直到线程开始运行之后,您才能获得线程 ID。调用返回时完全无法预测,因为您知道线程可能已经完成。如果线程在该进程的其余部分中引起任何需要 TID 的交互,那么您就有问题了。

如果 API 不支持暂停启动线程,这不是一个无法解决的问题,只需立即将线程块放在互斥锁上,并在 CreateThread 调用返回后释放该互斥锁。

但是,如果缺少 API 支持,Windows API 中的 CREATE_SUSPENDED 的另一个用途非常难以处理。 CreateProcess() 调用也接受这个标志,它挂起进程的启动线程。机制是相同的,进程被加载,你会得到一个 PID,但在你释放启动线程之前没有代码运行。这非常有用,我已经使用此功能设置了一个进程保护,以检测进程故障并创建一个小型转储。 CREATE_SUSPEND 标志让我能够检测和处理初始化失败,通常很难排除故障。

【讨论】:

pthreads 不会受到这种竞争的影响,因为线程 ID 是通过引用设置的。 pthread_create() 可以保证在线程启动之前设置好线程ID。 CreateThread() 接口确实有这个问题,所以我可以看到它可能需要能够“开始挂起”。 嗯,在 CreateThread 中也是通过引用设置的。不知道先吃什么鸡。【参考方案4】:

您可能希望以其他(通常较低)优先级或特定关联掩码启动线程。如果您像往常一样生成它,它可能会以不希望的优先级/亲和力运行一段时间。所以你启动它暂停,改变你想要的参数,然后恢复线程。

【讨论】:

这表明您需要它来解决 WinAPI 中的缺陷。 CreateThread() 的所有这些参数,而您仍然无法创建您想要的线程! 【参考方案5】:

我们使用的线程能够交换消息,并且我们有连接这些线程的任意可配置的优先级继承消息队列(在配置文件中描述)。在每个队列都被构建并连接到每个线程之前,我们不能让线程执行,因为它们将开始向无处发送消息并期望响应。在构建每个线程之前,我们无法构建队列,因为它们需要附加到某些东西上。因此,在配置最后一个线程之前,不允许任何线程工作。我们使用 boost.threads,他们做的第一件事就是等待boost::barrier

【讨论】:

【参考方案6】:

我曾经偶然发现过类似的问题。暂停初始状态的原因在其他答案中处理。

我的 pthread 解决方案是使用互斥锁和cond_wait,但我不知道这是否是一个好的解决方案,是否可以满足所有可能的需求。此外,我不知道是否可以将线程视为暂停(当时,我将手册中的“阻塞”视为同义词,但可能并非如此)

【讨论】:

以上是关于为啥我要启动一个“暂停”的线程?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中暂停和恢复 POSIX 线程?

使用属性“暂停”线程

使用单个条件变量暂停多个线程

如何使用 threading 模块暂停和恢复线程?

为啥不在构造函数中启动一个线程?如何终止?

c# .net 一条线程 abort 之后,怎么重启启动。