C++ 是否允许主线程在进入main()之前生成POSIX线程?
我有一个包含线程的对象。我希望物体的命运和线的命运是一致的。因此,构造函数创建一个线程(使用C++ 是否允许主线程在进入main()之前生成POSIX线程?,c++,multithreading,c++11,segmentation-fault,pthreads,C++,Multithreading,C++11,Segmentation Fault,Pthreads,我有一个包含线程的对象。我希望物体的命运和线的命运是一致的。因此,构造函数创建一个线程(使用pthread\u create),析构函数执行操作以使线程在合理的时间内返回,然后加入线程。只要我不使用静态存储持续时间实例化其中一个对象,这就可以正常工作。如果我在全局或命名空间或静态类作用域实例化其中一个对象,程序编译很好(gcc 4.8.1),但在运行时立即segfaults。通过print语句,我确定主线程在segfault之前甚至没有进入main()。有什么想法吗 更新:还向构造函数的第一行添
pthread\u create
),析构函数执行操作以使线程在合理的时间内返回,然后加入线程。只要我不使用静态存储持续时间实例化其中一个对象,这就可以正常工作。如果我在全局或命名空间或静态类作用域实例化其中一个对象,程序编译很好(gcc 4.8.1),但在运行时立即segfaults。通过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
,它打印在文档的开头。显然,在尝试访问这些静态存储时间互斥体之前,它们都没有被初始化。所以,是的,这可能是凯西的答案
我不想为复杂的对象和静态存储时间而烦恼。无论如何,这没什么大不了的。进入main之前发生的事情将是特定于平台的,但这里有一个关于main()在Linux上如何执行的链接 有用的鹬是 __libc_start_main初始化必要的东西,特别是C库(比如malloc)和线程环境,并调用我们的main 有关更多信息,请查阅\uu libc\u start\u main
不确定这在Windows上的行为,但似乎在进入main之前调用任何标准的C库都不是一个好主意可能有很多方法可以做到这一点。请参阅下面的代码段,其中类A的构造函数在main之前调用,因为我们在全局范围内声明了类A的对象:(我扩展了示例以演示如何在main执行之前创建线程)
#包括
#包括
#包括
使用名称空间std;
void*fun(void*x)
{
while(true){
cout非局部变量的初始化在C++11§3.6.2中有描述,第2段中有大量与线程有关的可怕内容:
如果程序启动线程(30.3),变量的后续初始化相对于在不同转换单元中定义的变量的初始化是不顺序的。否则,变量的初始化相对于在不同转换单元中定义的变量的初始化是不确定顺序的。如果程序启动线程,则变量的子序列无序初始化相对于每个其他动态初始化不排序
我解释为“变量的后续无序初始化相对于每个其他动态初始化是不顺序的”这意味着生成的线程无法访问任何动态初始化的变量,而该变量在生成线程之前未初始化,因此不会导致数据争用。如果该线程没有以某种方式与main
同步,则基本上就是手捂着眼睛在雷区中跳舞
我强烈建议您通读并理解3.6的所有内容;即使没有线程,在main
开始之前也需要做大量的PITA工作。这听起来很奇怪,也很危险(因此我并不奇怪它会导致问题),但是我从来没有听说过这样的东西,所以我不能说这是绝对错误的。我很想听听别人怎么说。全局和静态对象甚至在执行main()之前就已经创建了
method。我的建议是在类的构造函数和析构函数中添加更多调试打印,检查当对象被销毁时线程没有从被销毁的对象中访问任何内容。否则,请确保析构函数首先终止线程。所有工作线程
似乎都是另一个全局线程具有静态存储持续时间的l对象。你确定它是在worker
对象之前初始化的吗?@Casey确实是。不,还有什么方法可以确定吗?我只知道它在print语句之前会出现故障。Windows也有类似的机制。mainCRTStartup
是首先执行的函数,初始化CRT并调用程序的
mainsteroids@B就像Mechagodzilla版本的静态初始化命令fiasco。您的构造函数没有访问全局对象对象对象“a”是全局的,tћid也是全局的。
#include <iostream>
#include <stdlib.h>
#include <pthread.h>
using namespace std;
void *fun(void *x)
{
while (true) {
cout << "Thread\n";
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 main\n";
sleep(40);
return 0;
}