2016-02-17 2 views
1
#include <unistd.h> 
#include <pthread.h> 
#include <stdio.h> 

class Base { 
protected: 
    pthread_t receiverThreadID; 
    Base() { 
     pthread_create(&receiverThreadID,NULL,threadFunction,this); 
    } 
    ~Base() { 

    } 

    virtual void handleEvent() = 0; 
    static void* threadFunction(void* arg) { 
     while(true) { 
      // This threads receives UDP via recvfrom(). The messages can come in randomly. I use sleep() to simulate a blocking wait 
      sleep(1); 
      ((Base*)arg)->handleEvent(); 
     } 
     return 0; 
    } 
}; 

class Derived : public Base { 
    virtual void handleEvent() { 
     // Do something 
     printf("hello\n"); 
    } 

public: 
    Derived() {} 
    ~Derived() {} 

}; 

int main() { 
    Derived derived; 

    sleep(10); 
} 

Вы не должны вызывать чистую виртуальную функцию от конструктора класса, но нормально ли создать поток в конструктор, который в свою очередь вызывает чистую виртуальную функцию? Есть ли риск для состояния гонки? У меня не было ошибок во время выполнения с приведенным выше кодом.Вызов чистой виртуальной функции в отдельном потоке, который создается из конструктора базового класса

Если код не подходит, как я сделал, как его решить?

+2

Для обеспечения выполнения конструктора необходим механизм синхронизации. Здесь все, что имеет значение, когда поток создается, не имеет значения. – Donnie

ответ

1

Это может взорваться, если threadFunction() звонит handleEvent(), прежде чем конструктор Derived будет выполнен.

Почему бы не поместить вызов pthread_create в другую функцию - скажем, start() - и вызвать его, как только объект будет построен?

class Base { 
protected: 
    pthread_t receiverThreadID; 
    Base() {} 
    ~Base() {} 

    void start() { 
     pthread_create(&receiverThreadID,NULL,threadFunction,this); 
    } 

    ... 
}; 
+0

Могу ли я вызвать start() из конструктора Derived? – mbarnapam

+0

@mbarnapam: Нет, [просто не делай этого] (http://www.artima.com/cppsource/nevercall.html). В частности, что, если вы наследуете 'Derived' и переопределяете' handleEvent' снова? – Claudiu

+0

Вы можете вызвать start() в конструкторе Derived, однако обратите внимание, что будет вызываться производная версия виртуальной функции (которая может быть или не быть проблемой, в зависимости от того, выходите ли вы дальше от Derived). То есть вызов виртуальных функций в конструкторе четко определен, но вам нужно знать, что произойдет (что может быть иначе, чем вы могли бы ожидать). – axalis

1

Если вы вызываете его в потоке или непосредственно не имеет значения, вы не должны вызывать чисто виртуальную функцию перед Производный полностью построена - и это происходит после строительства базы.

Сон - это то, что делает этот код работающим, но не безопасным.

Если вы хотите безопасный вариант, я вижу 2 варианта:

  1. Используйте семафоры или аналогичные механизмы для предотвращения нить от вызова функции до того, как конструктор будет сделано.
  2. (И, вероятно, лучший вариант): Начните свой поток в функции, вызванной после конструктора. Возможно, вы захотите использовать заводскую функцию, чтобы убедиться, что она всегда называется.
0

Неправильно вызывать виртуальную функцию, когда конструктор не выполнен полностью.

Как и другие предлагаемые, убедитесь, что у вас есть другая функция, которая использовала эту виртуальную функцию. И вызовите эту функцию после создания объекта. например.

class Base{ 
    static void *create_thread(void * arg){ 
     pthread_create(&receiverThreadID,NULL,threadFunction,this); 
    } 

}; 
// client call something like this. 
Base * b = new Derived(); 
b->create_thread(); 

Надеюсь, что это должно решить проблему.

Смежные вопросы