2014-01-15 8 views
1

나는 명백하게하려고 노력할 것입니다. 몇 가지 단추와 QTextEdit가있는 Qt 응용 프로그램을 만듭니다. 다음으로 pthread를 만듭니다. MainWindow에 대한 포인터를 매개 변수로 제공하십시오. 이런 식으로 :pthread에서 QObject로 신호 보내기

MainWindow w; 
pthread_create(&rThread,NULL,treat,&w); 

치료는 스레드가 만들어 질 때 실행되는 기능입니다. 지금은을 myButton이라는 푸시 버튼이 있고, 나는 치료의 함수 내에서 다음과 같이 뭔가를해야만 할 경우 :

void *treat(void *arg) 
{ 
    MainWindow *win = (MainWindow*)arg; 
    win->ui->myButton->setEnabled(false); 
    close(pthread_self()); 
} 

그것은 잘 작동하고 myButton에 사용할 수 없게됩니다 내 응용 프로그램입니다. 나는이 같은 일을하지만 경우 :

void *treat(void *arg) 
{ 
    MainWindow *win = (MainWindow*arg; 
    win->ui->editText->setText("random string"); 
    close(pthread_self()); 
} 

다음과 같은 오류와 충돌합니다 내 응용 프로그램 :

QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x23af2e0), parent's thread is QThread(0x209a290), current thread is QThread(0x7f7eec000af0) The program has unexpectedly finished.

내가 UI를 메인 스레드에 살고 있으며, 아마도에서 accesible하지 않습니다 알고있는 것처럼 쓰레드, 나는이 스레드에 대한 메인 윈도우의 포인터를 제공했다. 그런데 왜 버튼을 사용할 수 없게 작동합니까? 나 엄청 혼란스러워. QThread를 사용하는 이유는 선생님이하지 않기 때문입니다. 나는 pthreads를 사용해야합니다. pthread에서 editText로 변경을 어떻게 적용 할 수 있습니까? Ui가 "살아있다"는 경우 어떻게 pthread에서 다른 스레드로 신호를 보낼 수 있습니까? 선배 들께 감사드립니다.

답변

3

말하기, 그것은 object->thread()가 아닌 다른 스레드에서 QObject (또는 파생 클래스 ') 방법 어떤를 호출하는 것은 오류입니다 - 그들은 설계 및 문서화하지 않는 한이 스레드로부터 안전합니다. Qt 고유에는 thread safe 용으로 명시 적으로 문서화 된 몇 가지 메소드가 있습니다 (예 : QCoreApplication::postEvent).

직면하는 동작은 비 GUI 스레드에서 QWidget 메서드에 액세스하기 때문입니다. 정의되지 않은 동작이므로 일부 메서드는 충돌하고 일부 메서드는 충돌하지 않을 수도 있지만 그렇지 않은 경우에도 여전히 신뢰할 수없는 정의되지 않은 동작입니다. 우리가 알고있는 모든 것에 달의 위상에 달려 있습니다.

다른 스레드에서 수행하는 유일한 안전한 작업은 이벤트를 개체에 게시하는 것입니다. 다른 스레드의 객체에 QMetaMethod::invoke 또는 QMetaObject::invokeMethod을 사용하면 Qt는 내부적으로 QMetaCallEvent을 객체에 게시합니다. 이벤트 게시는 스레드로부터 안전하므로 (다른 스레드에서 수행 할 수 있음), 다른 스레드의 해당 호출 메소드 중 하나를 사용하는 것이 좋습니다. QObject::event()은 적절한 메소드 호출을 실행하여 이러한 이벤트에 반응합니다.

그래서, 당신은 다른 스레드에서 할 수있는 유일한 방법은 다음과 같습니다

QMetaObject::invokeMethod(win->ui->editText, "setText", Q_ARG(QString, "random string")); 

아아, 이것은 나쁜 디자인은 외부로합니다 (ui 포인터 등) MainWindow를 내부 세부 사항을 노출하고 있기 때문에,이다. 다른 스레드에서, 당신이 할,

MainWindow : public QWidget { 
    ... 
public: 
    Q_SLOT void setEditText(const QString & str) { 
    ui->editText->setText(str); 
    } 
    ... 
}; 

다음 : 당신은 무엇을 대신해야하는 것은 창에 setEditText 슬롯이 있습니다

QMetaObject::invokeMethod(win, "setEditText", Q_ARG(QString, "random string")); 
내가 완전히 pthreads의 때를 사용하지 마렉 R의 권고에 동의

QThread을 사용할 수 있습니다.

+0

고마워요. 그것은 트릭을했다. 2 일 동안이 문제에 대한 해결책을 찾지 못했습니다. QThreads.pinally 대신 pthreads를 사용하게하기 위해 선생님이 싫어. 내 말 잘 들어, 선생님! :) – Zan

+0

@Zan :'QThread'는 삶을 좀 더 쉽게 만들어 주지만 pthreads와 똑같은 제약에 직면하게됩니다. 즉, 스레드로부터 안전하지 않은 메서드에 대한 * 직접 * 크로스 스레드 호출을 수행 할 수 없습니다 (별도로 명시하지 않는 한 * all * 메서드를 읽습니다). –

1

우선 불필요한 혼합 라이브러리는 나쁜 습관입니다. Qt는 QThread이고 매우 편리합니다. QtConcurrent입니다.

둘째로 이것은 잘못된 디자인입니다. 스레드에서 계산을 처리 할 QObject을 만들고 UI (메인 스레드)에 결과를 전달해야 할 때 신호를 내 보냅니다. 그런 다음 연결을 만들고 Qt는 나머지 것들을 처리하여 스레드를 안전하게 만듭니다 (기본적으로 스레드간에 신호가 전달되면 연결을 대기열에 넣습니다).

동시 Qt를 가진 당신 코드 :

일반적으로
void *treat(SomeClass *arg) { 
    arg->doStuff(); 
} 

QtConcurrent::run(treat, someObject); 
+0

그래서 QObject를 상속하는 새 클래스를 만들어야하며 MainWindow에 속한 슬롯으로 보낼 신호가 있어야합니다. 하지만 언제 connect 메소드를 호출해야합니까? 스레드가 시작되기 전에? 아니면 치료 기능 안에서 할 수 있을까요? – Zan

+0

게으른 경우 'MainWindow'에 신호를 추가하여 버튼에 연결하고 버튼을 직접 조작하는 대신이 신호를 사용할 수 있습니다. 그러한 체인을 preforming하는 것은'win-> ui-> editText->는 매우 나쁜 코드 (유지하기 어렵다)이다. –

+0

조언 해 주셔서 감사합니다. 나는이 십자가 틀에 다시는 가지 않기를 바란다. 그것은 고통이었습니다. – Zan

관련 문제