1

仮想メソッドを使用してクラスインスタンスを作成し、それを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;
}
4

2 に答える 2

5

ここでの唯一の問題は、生成されたスレッドがメソッドを実行する前にオブジェクトを削除しているため、その時点までに子デストラクタがすでに起動しており、オブジェクトが存在していないことです。

したがって、それは 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;
}

};

一方、それらを削除する前にメインにスリープを配置するだけでは、生成されたスレッドが有効なリソースを使用しているため、その問題は発生しません。

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;
}
于 2009-11-26T09:35:46.813 に答える
2

デストラクタでpthread_join(またはその他の実際の作業)を実行しないでください。join()メソッドをThreadに追加し、mainのthread[i]を削除する前に呼び出します。

デストラクタでpthread_joinを呼び出そうとすると、スレッドはまだThread :: routing()を実行している可能性があります。つまり、すでに部分的に破壊されているオブジェクトを使用しているということです。何が起こるか?知るか?うまくいけば、プログラムはすぐにクラッシュします。


さらに:

  • Threadから継承する場合は、Thread ::〜Threadを仮想として宣言する必要があります。

  • すべてのエラーをチェックし、適切に処理します(ところで、デストラクタ内では実行できません)。

于 2009-11-26T09:57:45.413 に答える