2012-03-08 24 views
2

我有一个C++应用程序,我在其中创建pthread以运行用户提供的功能。我希望能够在线程退出时以某种方式发出警报,以便我可以从我用来保持线程的pthread数组中移除它。有没有办法做到这一点,或者该功能是否应该设置一些“魔法值”。因为我产生pthread的主代码是一种runloop,所以我可以轻松地检查退出条件。pthread退出时运行函数


此外,正在使用std::vector<pthread_t> overdoing来跟踪我的线程是否超载?线程的数量不一定是任何常数,许多线程或很少可以运行。或者是否有另一个STL容器适合这些添加和删除(总是在一端添加,几乎在任何地方删除)。有没有其他的结构来跟踪pthreads?堆栈或列表是否在这里?还是一个标准的C阵列,慷慨的最大好处?由于问题的性质,我还可以维护一个固定大小的工作线程数组,我向其传递必须执行的用户函数。这是一个好的解决方案吗?

对不起,长期以来困惑的问题,但我只使用动态语言的线程,这永远不会是一个问题。


EDIT(12年3月8日): 阅读@ jojojapan的回答后,我决定使用某种形式的线程池。在我的结构中,我有一个生产者(runloop中的一个线程)和许多消费者(池中的工作线程)。是否有为多线程单生产者许多消费者使用的数据结构?或者我只是使用std::queuepthread_mutex_t就可以了?你可能要考虑

+0

当你说“运行用户提供的功能”,你是谁写的线程funcs? – Duck 2012-03-08 04:27:34

+0

动态语言?你的意思是GC语言;)。 – 2012-03-08 04:28:59

+0

@Duck:不,我正在写一个库,使用它的程序员写这些函数。 – Linuxios 2012-03-08 13:52:27

回答

3
  1. 一种选择是没有真正结束,删除线程,一旦他们完成了任务,而是让他们活着,让他们等待新的任务分配给他们。你可以做两件事情做到这一点:

    1. 使用的(几乎)无限循环在线程
    2. 使用并发队列或其他技术,使他们等待的信号由另一个线程被赋予。设计模式和策略在几个SO问题中进行讨论,例如this one
  2. 如果你真的想一旦线程结束发送信号,您可以使用pthread_cond_t并在其上调用pthread_cond_signal一个线程到达其return声明之前。当然,假设有一些其他线程正在运行,等待这些信号,并通过从矢量中删除相应的线程来作用于它们。有关使用的详细信息在相应的手册页上进行了说明,但也在this SO post中进行了说明。

编辑相关意见和问题的编辑部分:

  1. 关于工作线程的数量:这取决于资源使用的最由线程。如果这些线程所做的大部分工作是计算和一点内存访问,换句话说,如果它们是CPU限制的,那么使用尽可能多的线程是有意义的,因为CPU可以维护多个线程(具体来说,有一定数量的内核,和每个内核的(硬件)线程的数量,在你的CPU开始减速之前,它们可以运行。你正在创建的线程(软件线程)应该是大约一样多,或者多一些(最多两倍)硬件线程根据what @Tudor says here))是合理的)。但是,如果线程大量使用内存(内存绑定)或硬盘(IO绑定)或其他资源(如网络,NFS或其他服务器),则可能需要按顺序减少线程数)不要让他们互相阻碍,(b)不要给某些资源带来不合理的负担。确定正确的线程数可能是一个实验问题,并且保持数字可配置通常是一个好主意。

  2. 关于存储工作任务的最佳数据结构:我在上面引用的帖子的评论中提到的concurrent bounded queue可能非常好。虽然我没有尝试过。但是,如果您想简单起见,如果使用信号/互斥技术正确保护它们,标准std::queue或甚至简单地std::vector不是一个不错的选择。

+0

感谢您的回答。你的第一个选择很有意义。只有两件事:一,你认为我应该拥有多少工作者线程,以及二,我应该使用什么数据结构来存储需要执行的调用? (用'pthread_mutex_t'或一些特殊的线程队列类锁定的'std :: queue')。另外,我还会提出这个问题,有一个生产者和任何数量的消费者。 – Linuxios 2012-03-08 14:02:51

1

请考虑完全更改策略并使用现有的线程池库。他们会为你做这项工作,你将会节省很多不那么有趣的调试。

Boost.thread池是其中之一,link

1

一个简单的方法就是使用管道。

在产卵线程之前打开管道。传递管道fd作为线程数据的一部分。在线程退出之前,它将pthread_self()写入管道。管道读端有主线或单线。它读取死线程的tid并立即执行pthread_join。 (如果它是一个单独的收割线程,它可以阻止读取管道;如果它主要是使其成为您的选择/轮询或其他任何部分)

这使您可以灵活地不使用数据结构如果不想要,可以保存TID。如果你想保存它们,那么列表或地图是比矢量更好的选择。

如果您主要启动线程和一个单独的'收割者'线程收集它们,并且您想要将它们保存在某个结构中,那么您将需要同步访问这两者之间的结构。

+0

管道看起来像一个相当重的工具,只用于在同一进程的两个线程之间传递TID。我倾向于使用已完成的TID和pthread_cond_signal的共享列表来通知收割者已将新的TID添加到列表中。 – 2012-03-08 05:28:00

+1

管道不是特别重的重量,在这种情况下,它在N个螺纹上分摊。在PIPE_BUF大小下,数据自动同步。在管道复制的一点点花费来它的相对容易使用。 – Duck 2012-03-08 05:49:48