【问题标题】:How to tell the parent that the thread is done in C++ using pthreads?如何告诉父母线程是在 C++ 中使用 pthreads 完成的?
【发布时间】:2010-03-22 17:19:54
【问题描述】:

我有一个 TCP 服务器应用程序,它使用 POSIX 线程和 C++ 在一个新线程中为每个客户端提供服务。

服务器在其套接字上调用“listen”,当客户端连接时,它会创建一个 Client 类的新对象。新对象在自己的线程中运行并处理客户端的请求。

当客户端断开连接时,我想通过某种方式告诉我的 main() 线程该线程已完成,并且 main() 可以删除该对象并记录类似“客户端断开连接”之类的内容。

我的问题是,我如何告诉主线程一个线程已经完成?

【问题讨论】:

  • 这些客户端对象是“线程对象”还是只是在它们自己的线程中运行的对象?
  • 只是在自己的线程中运行的对象..

标签: c++ tcp pthreads


【解决方案1】:

我能看到的最直接的方法是加入线程。见here。这个想法是,在加入调用时,命令线程将等到工作线程退出,然后再恢复。

或者,您可以使用一些共享变量和互斥锁来汇总。

【讨论】:

    【解决方案2】:

    如果子线程在完成后真的退出(而不是等待更多工作),父线程可以在其上调用pthread_join,这将阻塞直到子线程退出。

    显然,如果父线程正在做其他事情,它不能一直阻塞在pthread_join,所以你需要一种方法向主线程发送消息,告诉它调用pthread_join。您可以为此使用多种 IPC 机制,但在您的特定情况下(TCP 服务器),我怀疑主线程可能是 select 循环,对吧?如果是这种情况,我建议使用pipe 创建一个逻辑管道,并将管道的读取描述符作为主线程从中选择的描述符之一。

    当子线程完成后,它会向管道写入某种消息,说“我完成了!”然后服务器会知道在该线程上调用pthread_join,然后在连接完成时执行它需要执行的任何其他操作。

    请注意,您没有必须在已完成的子线程上调用pthread_join,除非您需要它的返回值。但是,如果子线程可以访问共享资源,这样做通常是一个好主意,因为当pthread_join 返回时没有错误,它可以向您保证子线程确实已经消失并且不在发送之间的某个中间状态“我完成了!”消息并且实际上已经退出。

    【讨论】:

    • @Tyler,如果您不加入或分离线程,我相信您会泄漏资源。
    • @Michael 你是对的。如果你不打算调用加入,你应该分离。
    【解决方案3】:

    如果一切正常,pthreads 将返回 0;如果出现问题,它们将返回 errno

    int ret, joined;
    ret = pthread_create(&thread, NULL, connect, (void*) args);
    joined = pthread_join(&thread, NULL);
    

    如果joined 为零,则线程完成。清理该线程的对象。

    【讨论】:

    • 不,这是不对的。 "pthread_create" 返回 0 表示线程已经创建成功,并不意味着它已经完成(甚至,事实上,它有机会运行)。
    • 我的错,本意是写pthread_join,不知道在想什么
    • 错了,真的。如果 ret 为零,则表示线程创建成功,仅此而已。
    • 阻塞调用线程,等待新线程完成。如果侦听器线程这样做,它就会卡住并且不能再提供任何请求。侦听器仅在知道客户端线程完成后才应执行此操作。但这只是让我们回到最初的问题:您如何知道客户端线程何时完成?
    【解决方案4】:

    虽然可以实现 IPC 机制以在其他线程即将终止时通知主线程,但如果您想在线程终止时执行某些操作,则应尝试让终止线程自己执行。

    您可能会考虑使用pthread_cleanup_push() 来建立一个例程,以便在线程被取消或退出时调用。另一种选择可能是使用pthread_key_create() 创建线程特定的数据键和关联的析构函数。

    如果由于阻塞而不想从主线程调用pthread_join(),则应通过在创建线程时将其设置为选项或调用pthread_detach()来分离客户端线程。

    【讨论】:

      【解决方案5】:

      您可以使用“要删除的线程对象”队列,使用互斥锁保护对队列的访问,然后向 pthread 条件变量发出信号以指示队列中有可用的内容。

      但你真的想这样做吗?更好的模型是让每个线程自己清理,而不用担心与主线程同步。

      【讨论】:

      • 好的。我认为自清洁线程的想法很好。但是可以直接从对象本身调用析构函数吗?
      • 是的,您可以从对象本身调用“删除这个”,只要您遵循一些规则。例如,对象不能在栈上分配,主线程一旦创建处理线程就不允许引用对象(因为线程随时可能终止)。见stackoverflow.com/questions/447379/…
      【解决方案6】:

      调用pthread_join 将阻塞主线程的执行。鉴于问题的描述,我认为它不会提供所需的解决方案。

      在大多数情况下,我首选的解决方案是让线程执行自己的清理。如果这是不可能的,你要么必须使用某种带有共享变量的轮询方案(只要记住让它们线程安全,提示:volatile),或者可能是某种依赖于操作系统的回调机制。请记住,您希望在调用 listen 时被阻止,因此请考虑让线程自行清理。

      【讨论】:

      • 将共享变量声明为“volatile”不会自动使它们成为线程安全的。网络和 SO 上有许多讨论解释了为什么会这样。一定要依赖适当的同步机制(互斥锁等)或原子操作。
      【解决方案7】:

      正如其他人所提到的,使用pthread_join 处理给定 线程的终止很容易。但是 pthread 的一个弱点是将来自多个来源的信息汇集到一个同步流中。 (或者,你可以说它的强项是性能。)

      到目前为止,对您来说最简单的解决方案是在工作线程中处理清理工作。记录断开连接(在日志中添加互斥锁),酌情删除资源,并在不向父级发送信号的情况下退出工作线程。

      添加互斥体以允许操纵共享资源是一个棘手的问题,因此要灵活且富有创意。同步时请务必小心谨慎,并在优化前进行分析。

      【讨论】:

        【解决方案8】:

        我遇到了与您描述的完全相同的问题。在大约 300 个打开的客户端连接之后,我的 Linux 应用程序无法创建新线程,因为从未调用过 pthread_join。对我来说,使用 pthread_tryjoin_np 有所帮助。 简要说明:

        • 拥有一个包含所有打开的线程描述符的映射
        • 在打开新的客户端线程之前,我从主线程遍历 map 并为 map 中记录的每个线程调用 pthread_tryjoin_np。如果线程完成,则调用结果为零,这意味着我可以从该线程中清理资源。同时 pthread_tryjoin_np 负责释放线程资源。如果 pthread_tryjoin_np 调用返回的数字不是 0,这意味着线程仍在运行,我什么也不做。

        潜在的问题是我没有将 pthread_tryjoin_np 视为官方 POSIX 标准的一部分,因此该解决方案可能不可移植。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-05-12
          • 1970-01-01
          • 2010-12-13
          • 1970-01-01
          • 2023-04-10
          • 1970-01-01
          • 1970-01-01
          • 2016-10-10
          相关资源
          最近更新 更多