简体   繁体   English

在虚拟函数和pthread_create之间进行竞争

[英]Race between virtual function and pthread_create

When I try to create a class instance with a virtual method and pass it to pthread_create, I get a race condition, causing the caller to sometimes call the base method instead of the derived method like it should. 当我尝试使用虚拟方法创建类实例并将其传递给pthread_create时,出现竞争情况,导致调用方有时调用基本方法,而不是像应有的那样调用派生方法。 After googling pthread vtable race , I found out this is fairly well-known behavior. 在搜索了pthread vtable race ,我发现这是相当知名的行为。 My question is, what's a good way to get around it? 我的问题是,如何解决它?

The code below exhibits this behavior at any optimization setting. 下面的代码在任何优化设置下都表现出此行为。 Note that the MyThread object is completely constructed before being passed to pthread_create. 请注意,在将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;
}

The only problem here is that you are deleting the objects before the spawned thread executes the method, so by that time the child destructor already fired and the object is not there anymore. 唯一的问题是,您是在生成的线程执行该方法之前删除对象的,所以到那时子析构函数已经被触发,对象不再存在。

So it has nothing to do with pthread_create or whatever, its your timing, you can't spawn a thread, give it some resources and delete them before he has the chance of using them. 因此,它与pthread_create无关,无论您的时间安排如何,您都无法生成线程,为其提供一些资源并删除它们,然后他才有机会使用它们。

Try this, it'll show how the objs are destructed by main thread before spawned thread uses them: 尝试一下,它将显示在生成的线程使用它们之前,主线程如何破坏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;
}

};

In the other hand if you just place a sleep in main before deleting them you'll never have that problem because the spawned thread is using valid resources. 另一方面,如果您只是在删除它们之前先在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;
}

Do not do pthread_join (or any other real work) in destructor. 不要在析构函数中执行pthread_join(或任何其他实际工作)。 Add join() method to Thread and call it before delete thread[i] in main. 在线程中添加join()方法并在删除main中的thread [i]之前调用它。

If you try to call pthread_join in destructor, thread may be still executing Thread::routine(). 如果尝试在析构函数中调用pthread_join,则线程可能仍在执行Thread :: routine()。 Which means it's using object that is already partially destroyed . 这意味着它正在使用已被部分破坏的对象。 What will happen? 会发生什么? Who knows? 谁知道? Hopefully program will crash quickly. 希望程序会迅速崩溃。


Additionally: 另外:

  • If you wish to inherit from Thread, Thread::~Thread should be declared virtual. 如果您希望从Thread继承,则应该将Thread ::〜Thread声明为virtual。

  • Check for all errors and handle them properly (which BTW cannot be done inside destructor). 检查所有错误并正确处理(不能在析构函数中完成BTW)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM