가상 함수와 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;
}
};
반면에 스폰 된 스레드가 유효한 리소스를 사용하기 때문에 그 문제를 삭제하기 전에 메인에 잠을 자면 그 문제가 없습니다.
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 (또는 다른 실제 작업)을하지 마십시오. joit () 메소드를 추가하여 스레드를 삭제하고 메인에서 스레드 [i]를 삭제하기 전에 호출하십시오.
Destructor에서 pthread_join을 호출하려고하면 스레드가 여전히 실을 실행 중일 수 있습니다. 즉, 객체를 사용하고 있음을 의미합니다 이미 부분적으로 파괴되었습니다. 무슨 일이 일어날 것? 누가 알아? 희망적으로 프로그램이 빠르게 충돌합니다.
또한 :
스레드에서 상속하려면 스레드 :: ~ 스레드가 가상으로 선언되어야합니다.
모든 오류를 확인하고 올바르게 처리하십시오 (BTW는 파괴자 내부에서 수행 할 수 없습니다).