2015-12-02 17 views
1

我的錯誤:爲什麼一個類的線程函數應該靜態地在同一個類中被訪問?

error: cannot convert 'MainWindow::producerThreadFunction' from type 'void* (MainWindow::)(void*)' to type 'void* (*)(void*)' 
    if (pthread_create (&producer, NULL, producerThreadFunction, NULL)) 
                    ^

頭文件:

#ifndef MAINWINDOW_H 
#define MAINWINDOW_H 

#include <QMainWindow> 
#include <QApplication> 

#include <pthread.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/syscall.h> 
#include <iostream> 
#include <QDebug> 

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

    pthread_mutex_t mutexVariable = PTHREAD_MUTEX_INITIALIZER; 
    pthread_cond_t conditionVariable = PTHREAD_COND_INITIALIZER; 

    QList <int> queueLIFO; 

public: 
    MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 

    // This function is run by the thread `Producer`. 
    void *producerThreadFunction (void *arg); 

    // This function is run by the thread `Consumer`. 
    void *consumerThreadFunction (void *arg); 

    int start(); 
}; 

#endif // MAINWINDOW_H 

源文件:(功能發生錯誤)

int MainWindow :: start() 
{ 
    pthread_t producer; 
    pthread_t consumer; 

    if (pthread_create (&producer, NULL, producerThreadFunction, NULL)) 
    { 
     fprintf (stderr, "Error creating thread Producer\n"); 
     return 1; 
    } 
if (pthread_create (&consumer, NULL, consumerThreadFunction, NULL)) 
{ 
    fprintf (stderr, "Error creating thread Consumer\n"); 
    return 1; 
} 

if (pthread_join (producer, NULL)) 
{ 
    fprintf (stderr, "Error joining thread Producer\n"); 
    return 2; 
} 

if (pthread_join (consumer, NULL)) 
{ 
    fprintf (stderr, "Error joining thread Consumer\n"); 
    return 2; 
} 

return 0; 

}

根據this thread,解決方案是使producerThreadFunction靜態。

爲什麼一個類的線程函數應該是靜態的以便可以在同一個類中訪問?

該函數是該類的成員函數。爲什麼我不能直接訪問它?

+0

只需使用標準的C++線程。 –

+2

請注意,儘管大多數編譯器不會阻止您將靜態成員函數用作「C」函數,但它的語言綁定是錯誤的。而在標準C++中,它不能被聲明爲「extern」C「'。我似乎記得Sun的C++編譯器會默認提醒它。 –

+0

@ Cheersandhth.-Alf好 - 我沒有意識到這一點。我做了一點挖掘,發現你是對的。我相應地更新了我的答案。 –

回答

3

pthread_create需要一個函數指針,而不是指向成員函數的指針。這些在C++中是非常不同的類型,因爲成員函數指針包含一個隱含的指針this。在實踐中,靜態成員函數相當於非成員函數,因此工作正常(根據標準,N.B.這在技術上不是真實的 - 見下文)。

如果pthread是一個C++庫,它可能需要一個std::function(或在C++ 11之前,boost::function),它可以接受各種函數類對象;例如函數指針,成員函數指針或函子類。但是,由於pthread是一個C庫,但是,您被困在編寫靜態函數並手動將this指針作爲參數傳遞。

您應該認真考慮使用std::thread(或在C++ 11之前,boost::thread)而不是pthreads。可用的相同同步原語可用於pthreads,例如, std::condition_variable。該std::thread構造函數可以直接接受一個成員函數指針:

std::thread producer(&MainWindow::producerThreadFunction, this); 

標準

C和C++可使用不同calling conventions。這意味着它不是而是可以安全地從C代碼調用C++函數,除非它被包裝在extern "C" block中。然而,如this answer on StackOverflow指出,C++ 11 7.5/4 「聯動規格」 雲:

A C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions.

因此,它不能保證由標準來工作。唯一符合標準的選擇是將代碼放在一個非成員函數,在內部,調用一個成員函數:

extern "C" { 

void producerThreadFunctionWrapper(void *arg) 
{ 
    static_cast<MainWindow *>(arg)->producerThreadFunction(); 
} 

} // extern "C" 

// ... 

pthread_create(&consumer, NULL, consumerThreadFunctionWrapper, this); 

在實踐

在實踐中,我從來沒有遇到過的架構/編譯器在哪裏靜態成員函數不使用C鏈接。這個問題的答案question 33.2 in the C++FQA幽默地作出了同樣的觀點:

Regarding the static-members-as-callbacks issue: if your implementation uses different binary calling conventions for C functions and C++ static member functions, call the support and inform them that their developers consume mind-altering chemicals at work.

買者自負,但是,也有這個被烙上了人們的StackOverflow(例如in 32 bit Visual Studio)少數報告。最安全的選擇是使用std::thread或爲您的回調編寫extern "C"包裝。

+0

答案是「因爲pthreads是一個C庫」。由於許多原因,這是一件好事。 – paulsm4

+0

@ paulsm4我並不是故意暗示''pthread' *應該是一個C++庫 - 我很高興它是用C編寫的。但是,它確實使這樣的操作更加冗長。 :) –

+0

@MichaelKoval:[我的問題](http://stackoverflow.com/questions/34037212/static-member-function-with-c-language-binding)的答案表明,靜態成員函數將正式始終有C++語言鏈接,即使使用'extern「C」'也是如此(它被忽略);因此即使將它作爲C回調在所有現存的編譯器中使用,它也是正式的錯誤™。 –

1

由於我們幾乎完成了2015年,現在是拋開C++ 2003的時候了。最好的方式叫你的線程如下:

int MainWindow :: start() 
{ 
    std::thread producer(&MainWindow::producerThreadFunction, this); 
    ... 

看看它是多麼容易?而且根本不需要擔心靜態成員函數!

+0

而對於pre C++ 11'boost :: thread'可以用完全相同的方式 – Slava

+0

缺少移動構造函數和右值語義,boost :: thread永遠不可能完全一樣。 – SergeyA

+0

這個特定情況下「完全」使用的術語不是一般用法。 – Slava

相關問題