Pergunta

Quando tento criar uma instância de classe com um método virtual e passá-lo para pthread_create, recebo uma condição de corrida, fazendo com que o chamador, por vezes, chamar o método base, em vez do método derivado como deveria. Após googling pthread vtable race, eu descobri que este é um comportamento bastante conhecido. A minha pergunta é, o que é uma boa maneira de contornar isso?

O código a seguir exibe este comportamento em qualquer configuração de otimização. Note-se que o objeto MyThread é completamente construído antes de ser passado para 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;
}
Foi útil?

Solução

O único problema aqui é que você está excluindo os objetos antes que o segmento gerou executa o método, por isso, que o tempo o destruidor criança já demitido e o objeto não está mais lá.

Por isso, não tem nada a ver com pthread_create ou qualquer outra coisa, o seu o seu tempo, você não pode gerar um segmento, dar-lhe alguns recursos e excluí-los antes que ele tenha a chance de usá-los.

Tente isso, ele vai mostrar como os objs são destruídos por thread principal antes usos segmento gerado eles:

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;
}

};

Por outro lado, se você só colocar um sono principal antes de excluí-los você nunca vai ter esse problema porque o segmento gerado está usando os recursos válidos.

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;
}

Outras dicas

Não faça pthread_join (ou qualquer outro trabalho real) em destructor. Adicionar join () método de thread e chamá-lo antes fio de exclusão [i] na principal.

Se você tentar chamar pthread_join em destructor, rosca pode ser ainda execução Thread :: rotina (). O que significa que ele está usando objeto que é já parcialmente destruído . O que vai acontecer? Quem sabe? Esperemos programa irá falhar rapidamente.


Além disso:

  • Se desejar para herdar de Thread, Thread :: ~ Tópico deve ser declarado virtual.

  • Verifique se todos os erros e tratá-los adequadamente (que BTW não pode ser feito dentro destructor).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top