11

我是多线程的新手,我需要全面了解“加入”,我是否需要加入应用程序中的每个线程?多线程如何工作?

4

5 回答 5

11

不,如果你想让它不理会,你可以分离一个线程。如果你启动一个线程,要么分离它,要么在程序结束之前加入它,否则这是未定义的行为。

要知道线程需要分离,您需要问自己这个问题:“我是否希望线程在程序主函数完成后运行?”。这里有些例子:

  • 当您执行文件/新建时,您会创建一个新线程并将其分离:当用户关闭文档时,线程将被关闭在这里您不需要加入线程

  • 当您进行蒙特卡洛模拟、一些分布式计算或任何分而治之类型的算法时,您会启动所有线程并且您需要等待所有结果,以便您可以组合它们。在这里,您明确需要在合并结果之前加入线程

于 2014-03-15T16:13:25.393 回答
2

pthread_join() 函数暂停调用线程的执行,直到目标线程终止,除非目标线程已经终止。从带有非 NULL value_ptr 参数的成功 pthread_join() 调用返回时,终止线程传递给 pthread_exit() 的值在 value_ptr 引用的位置可用。当 pthread_join() 成功返回时,目标线程已终止。指定同一目标线程的多个同时调用 pthread_join() 的结果未定义。如果调用 pthread_join() 的线程被取消,那么目标线程将不会被分离。

所以pthread_join做了两件事:

  1. 等待线程完成。

  2. 清理与线程关联的所有资源。

这意味着如果您在不调用pthread_join的情况下退出进程,那么 (2) 将由操作系统为您完成(尽管它不会执行线程取消清理),并且 (1) 将不会完成。

所以是否需要调用pthread_join取决于是否需要 (1) 发生。


分离的线程

如果您不需要线程运行,那么您也可以pthread_detach它。无法加入分离的线程(因此您不能等待其完成),但如果它完成,它的资源会自动释放。

于 2014-03-15T16:15:20.947 回答
2

不加入一个线程就像没有delete记住你的所有记忆new。它可能是无害的,也可能是一个坏习惯。

未同步的线程处于未知执行状态。如果它是一个文件写入线程,它可能是在写入文件的过程中,然后应用程序完成。如果它是一个网络通信线程,它可能是握手的一半。

加入每个线程的缺点是,如果其中一个线程进入错误状态并被阻塞,您的应用程序可能会挂起。

一般来说,您应该尝试向未完成的线程发送消息,告诉它们退出并清理。然后你应该等待一段时间让他们完成或以其他方式回应他们很高兴死,然后关闭应用程序。现在在此之前,您应该表明您的程序不再对业务开放——关闭 GUI 窗口,响应来自您正在关闭的其他进程的请求,等等——因此,如果这花费的时间比预期的长,用户不会受到打扰。最后,如果事情进展不顺利——如果线程拒绝响应你关闭它们的请求并且你放弃了它们——那么你也应该记录错误,这样你就可以修复可能是更大问题的症状。

上一次工作线程意外挂起,我最初认为是网络中断和超时代码中的错误。经过更深入的检查,这是因为在关闭同步之前使用的对象之一是deleted:导致的未定义行为在我的复制案例中看起来就像挂起。如果我们不小心加入,这个 bug 将更难追踪(现在,正确的做法是使用我们无法删除的共享资源:但错误会发生)。

于 2014-03-15T16:54:36.273 回答
1

我需要加入我的应用程序中的每个线程吗?

不一定 - 取决于您的设计和操作系统。Join() 在 GUI 应用程序中是非常危险的 - 如果您不需要知道或不关心知道一个线程是否已从另一个线程终止,则不需要加入它。

我非常努力地不加入/等待任何线程。池线程、应用程序生命周期线程等通常不需要任何显式终止 - 取决于操作系统以及线程是否拥有或显式绑定到任何需要显式终止/关闭/其他的资源。

于 2014-03-15T16:16:24.060 回答
1

线程可以是可连接的或可分离的。分离的线程不应连接。另一方面,如果你没有加入可连接线程,你的应用程序会泄漏一些内存和一些线程结构。.join()c++11 std::thread 将调用 std::terminate,如果它没有被标记为分离并且线程对象在没有调用的情况下超出范围。请参阅pthread_detachpthread_create。这与流程非常相似。当孩子退出时,它将保持为僵尸,而它的创建者不会调用waitpid. 这种行为的原因是线程和进程的创建者可能想知道那里的退出代码。

更新:如果pthread_create使用等于 NULL 的属性参数调用(使用默认属性),将创建可连接线程。要创建分离线程,可以使用属性:

pthread_attr_t attrs;
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
pthread_create(thread, attrs, callback, arg);

此外,您可以通过调用已创建的线程来分离线程pthread_detach。如果您尝试加入一个分离的线程,pthread_join将返回EINVAL错误代码。glibc 有一个不可移植的扩展pthread_getattr_np,允许获取正在运行的线程的属性。因此,您可以检查线程是否与pthread_attr_getdetachstate.

于 2014-03-15T16:18:39.387 回答