为什么要将类的线程函数设置为静态才能在同一个类中访问?

时间:2015-12-02 07:05:35

标签: c++ multithreading pthreads

我的错误:

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静态。

为什么要将类的线程函数设置为静态才能在同一个类中访问?

该函数是该类的成员函数。为什么我不能直接访问它?

2 个答案:

答案 0 :(得分: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_variablestd::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&#34; Linkage规范&#34;表示:

  

在确定语言链接时忽略C语言链接   类成员的名称和类成员的函数类型   功能

因此,不保证按标准工作。唯一符合标准的选项是将代码放在非成员函数中,该函数在内部调用成员函数:

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的答案幽默地提出了同样的观点:

  

关于static-members-as-callbacks问题:如果你的   实现对C使用不同的二进制调用约定   函数和C ++静态成员函数,调用支持和通知   他们的开发人员在工作中消耗改变思维的化学物质。

但是,有一些关于StackOverflow(例如in 32 bit Visual Studio)的人被烧毁的报告。最安全的选择是使用std::thread或为回调编写extern "C"包装。

答案 1 :(得分:1)

由于我们几乎完成了2015年,现在是时候抛弃C ++ 2003了。调用线程的最佳方法如下:

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

看看它有多容易?而且根本不需要担心静态成员函数!