虚函数和 pthread_create 之间的竞争
-
05-07-2019 - |
题
当我尝试使用虚拟方法创建类实例并将其传递给 pthread_create 时,我遇到了竞争条件,导致调用者有时调用基方法而不是应有的派生方法。谷歌搜索后 pthread vtable race
, ,我发现这是众所周知的行为。我的问题是,有什么好的方法可以解决这个问题?
下面的代码在任何优化设置下都表现出这种行为。请注意,MyThread 对象在传递给 pthread_create 之前已完全构造完毕。
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Thread {
pthread_t thread;
void start() {
int s = pthread_create(&thread, NULL, callback, this);
if (s) {
fprintf(stderr, "pthread_create: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
static void *callback(void *ctx) {
Thread *thread = static_cast<Thread*> (ctx);
thread->routine();
return NULL;
}
~Thread() {
pthread_join(thread, NULL);
}
virtual void routine() {
puts("Base");
}
};
struct MyThread : public Thread {
virtual void routine() {
}
};
int main() {
const int count = 20;
int loop = 1000;
while (loop--) {
MyThread *thread[count];
int i;
for (i=0; i<count; i++) {
thread[i] = new MyThread;
thread[i]->start();
}
for (i=0; i<count; i++)
delete thread[i];
}
return 0;
}
解决方案
这里唯一的问题是你在生成的线程执行方法之前删除了对象,所以那时子析构函数已经被触发并且对象不再存在了。
所以它与pthread_create或其他什么无关,它是你的时间,你不能产生一个线程,给它一些资源并在他有机会使用它们之前删除它们。
试试这个,它将显示在生成的线程使用它们之前主要线程如何破坏obj:
struct Thread {
pthread_t thread;
bool deleted;
void start() {
deleted=false;
int s = pthread_create(&thread, NULL, callback, this);
if (s) {
fprintf(stderr, "pthread_create: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
static void *callback(void *ctx) {
Thread *thread = static_cast<Thread*> (ctx);
thread->routine();
return NULL;
}
~Thread() {
pthread_join(thread, NULL);
}
virtual void routine() {
if(deleted){
puts("My child deleted me");
}
puts("Base");
}
};
struct MyThread : public Thread {
virtual void routine() {
}
~MyThread(){
deleted=true;
}
};
另一方面,如果你只是在删除它们之前在main中放置一个睡眠,那么你将永远不会遇到这个问题,因为生成的线程正在使用有效的资源。
int main() {
const int count = 20;
int loop = 1000;
while (loop--) {
MyThread *thread[count];
int i;
for (i=0; i<count; i++) {
thread[i] = new MyThread;
thread[i]->start();
}
sleep(1);
for (i=0; i<count; i++)
delete thread[i];
}
return 0;
}
其他提示
不要在析构函数中执行 pthread_join (或任何其他实际工作)。将 join() 方法添加到 Thread 中,并在 main 中删除 thread[i] 之前调用它。
如果尝试在析构函数中调用pthread_join,线程可能仍在执行 线程::例程()。这意味着它正在使用的对象是 已经部分被毁。会发生什么?谁知道?希望程序很快就会崩溃。
此外:
如果你想继承Thread,Thread::~Thread应该被声明为virtual。
检查所有错误并正确处理它们(顺便说一句,不能在析构函数内完成)。
不隶属于 StackOverflow