我有一个包含线程的对象。我希望物体的命运和线的命运是一体的。因此,构造函数创建一个线程(使用pthread_create
),析构函数执行操作,使线程在合理的时间内返回,然后加入线程。只要我不实例化其中一个具有静态存储持续时间的对象,这就可以正常工作。如果我在全局或命名空间或静态类作用域实例化其中一个对象,程序会编译得很好(gcc 4.8.1),但在运行时会立即segfault。通过print语句,我已经确定主线程甚至不会在segfault之前输入main()。有什么想法吗?
更新:还在构造函数的第一行添加了一条print语句(所以在调用pthread_create
之前),甚至在segfault之前都没有打印,但构造函数确实使用了初始化列表,所以可能是什么原因导致的?
这是构造函数:
worker::worker(size_t buffer_size):
m_head(nullptr),m_tail(nullptr),
m_buffer_A(operator new(buffer_size)),
m_buffer_B(operator new(buffer_size)),
m_next(m_buffer_A),
m_buffer_size(buffer_size),
m_pause_gate(true),
m_worker_thread([this]()->void{ thread_func(); }),
m_running(true)
{
print("this wont get printed b4 segfault");
scoped_lock lock(worker_lock);
m_worker_thread.start();
all_workers.push_back(this);
}
和析构函数:
worker::~worker()
{
{
scoped_lock lock(worker_lock);
auto w=all_workers.begin();
while(w!=all_workers.end())
{
if(*w==this)
{
break;
}
++w;
}
all_workers.erase(w);
}
{
scoped_lock lock(m_lock);
m_running=false;
}
m_sem.release();
m_pause_gate.open();
m_worker_thread.join();
operator delete(m_buffer_A);
operator delete(m_buffer_B);
}
更新2:
好吧,我想明白了。我的print函数是原子函数,同样使用其他地方定义的外部名称空间范围互斥来保护cout
。我改为纯cout
,它打印在ctor的开头。显然,在尝试访问这些静态存储持续时间互斥对象之前,它们都没有初始化。所以,是的,这可能是凯西的答案。
我只是不想为复杂的对象和静态存储持续时间而烦恼。不管怎样,这没什么大不了的。
C++11§3.6.2中描述了非局部变量的初始化,第2段中有大量与线程有关的可怕内容:
如果程序启动线程(30.3),则变量的后续初始化相对于在不同转换单元中定义的变量的初始化是不按顺序的。否则,变量的初始化相对于在不同翻译单元中定义的变量的初始化是不确定的。如果程序启动线程,则变量的后续无序初始化相对于其他所有动态初始化都是不按顺序的。
我将"变量的后续无序初始化相对于其他所有动态初始化都是无序列的"解释为,派生线程无法访问任何具有动态初始化的变量,这些变量在派生线程之前未初始化,而不会导致数据争用。如果这个线程在某种程度上与main
不同步,那么你基本上就是在用手捂住眼睛跳过雷区。
我强烈建议你通读并理解3.6的全部内容;即使没有线程,在main
启动之前也需要做大量的PITA工作。
在进入main之前发生的事情将是特定于平台的,但这里有一个关于main()如何在Linux 上执行的链接
http://linuxgazette.net/84/hawk.html
有用的狙击手是
__libc_start_main初始化必要的东西,特别是C库(如malloc)和线程环境,并调用我们的main。
有关详细信息,请查找__libc_start_main
不确定这在Windows上的表现,但似乎在进入main之前任何标准的C库调用都不是一个好主意
可能有很多方法可以做到这一点。请参阅下面的片段,其中类A的构造函数在main之前调用,因为我们已经在全局范围内声明了类A的对象:(我已经扩展了这个示例,以演示如何在main执行之前创建线程)
#include <iostream>
#include <stdlib.h>
#include <pthread.h>
using namespace std;
void *fun(void *x)
{
while (true) {
cout << "Threadn";
sleep(2);
}
}
pthread_t t_id;
class A
{
public:
A()
{
cout << "Hello before main n " ;
pthread_create(&t_id, 0, fun, 0);
sleep(6);
}
};
A a;
int main()
{
cout << "I am mainn";
sleep(40);
return 0;
}
我在发布自己关于线程的问题后发现了这个问题。复习我的问题可能对其他人有帮助。我发现,当我在全局范围内分配一个在构造函数中创建线程的对象时,我会有奇怪的行为,但如果我将对象创建移动到main()内部,事情就会如我所期望的那样工作。这似乎与对这个问题的评论一致。