2011-05-10 78 views
14

我试图从C++ 11使用std ::线程。如果在执行其某个函数成员的类中可能有std :: thread,我找不到任何地方。考虑下面的例子... 在我的尝试(下面)中,函数是run()。C + + 11:std ::线程内执行一个函数成员与线程初始化在构造函数

我用-std = C++ 0x标志编译gcc-4.4。

#ifndef RUNNABLE_H 
#define RUNNABLE_H 

#include <thread> 

class Runnable 
{ 
    public: 
     Runnable() : m_stop(false) {m_thread = std::thread(Runnable::run,this); } 
     virtual ~Runnable() { stop(); } 
     void stop() { m_stop = false; m_thread.join(); } 
    protected: 
     virtual void run() = 0; 
     bool m_stop; 
    private: 
     std::thread m_thread; 
}; 


class myThread : public Runnable{ 
protected: 
    void run() { while(!m_stop){ /* do something... */ }; } 
}; 

#endif // RUNNABLE_H 

我得到这个错误及其他:(同样的错误使用和不使用$这个)

Runnable.h|9|error: no matching function for call to ‘std::thread::thread(<unresolved overloaded function type>, Runnable* const)’| 

当指针传递。

Runnable.h|9|error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say ‘&Runnable::run’| 

回答

15

这种方法是错误的。

问题是,虽然对象仍在构建中,但它的类型仍然不是最派生的类型,而是正在执行的构造函数的类型。这意味着当您启动线程时,对象仍然是Runnable,而对run()的调用可能会被调度到Runnable::run(),这是纯虚拟的,并且反过来会导致未定义的行为。

更糟糕的是,您可能会遇到错误的安全感,因为在某些情况下,正在启动的线程可能需要足够长的时间才能完成当前线程的Runnable构造函数,然后输入myThread对象,在这种情况下,新线程将执行正确的方法,但更改执行程序的系统(不同数量的内核,或系统负载或任何其他不相关的情况),并且程序将在生产中崩溃。

+1

这就是为什么需要'start()'是一个函数来完成实际的开始工作。 – 2011-05-10 21:41:25

+0

我不认为线程花费足够长的时间来调度会改变行为,不是它的构造函数做一个副本(因此切片)绑定的结果(&Runnable :: run,this)? – Cubbi 2011-05-10 21:48:17

+1

@Etienne de Martel:有不同的方法,'start()'方法选项显然是其中之一。另一种方法是boost和C++ 0x所采用的方法:将* runnable *对象从* thread *对象中分离出来,这会将操作作为参数运行到构造函数中。这可以确保将要执行的对象完全构建。当然,如果你不强制你的方式进入破坏它的框架...... – 2011-05-10 21:50:11

13

下面是一些代码来仔细考虑:

#ifndef RUNNABLE_H 
#define RUNNABLE_H 

#include <atomic> 
#include <thread> 

class Runnable 
{ 
public: 
    Runnable() : m_stop(), m_thread() { } 
    virtual ~Runnable() { try { stop(); } catch(...) { /*??*/ } } 

    Runnable(Runnable const&) = delete; 
    Runnable& operator =(Runnable const&) = delete; 

    void stop() { m_stop = true; m_thread.join(); } 
    void start() { m_thread = std::thread(&Runnable::run, this); } 

protected: 
    virtual void run() = 0; 
    std::atomic<bool> m_stop; 

private: 
    std::thread m_thread; 
}; 


class myThread : public Runnable 
{ 
protected: 
    void run() { while (!m_stop) { /* do something... */ }; } 
}; 

#endif // RUNNABLE_H 

一些注意事项:

  • 声明m_stop作为一个简单的bool因为你是可怕的不足;读了记忆障碍
  • std::thread::join可以抛出这样调用它没有从析构try..catch是鲁莽
  • std::threadstd::atomic<>是不可复制的,所以Runnable应标明,如果没有其他原因,而不是避免C4512 VC++的警告
+3

我不认为'm_stop'变量需要'volatile','std :: atomic '会处理多线程问题,'volatile'在这里没有额外的帮助。 – 2011-05-10 22:19:45

+0

@DavidRodríguez:FDIS中的第29.6.5/3段明确表示否则。如果std :: atomic <>对象不是易失性的,那么对该对象的访问可能会被重新排序; std :: atomic <>类型没有什么特别的改变了这个事实。 (由于不可编辑的错字重新粘贴) – ildjarn 2011-05-13 01:00:47

+2

我没有注意到前面评论中的错字,但我认为它比新版本更精确。标准在该段中说的是对原子对象的操作可以被合并*(并不是重新排序)。我对它的理解是'std :: atomic i = 0;对于(; i <10; i = i + 1){}'可以转换为'std :: atomic i = 10;'(这对于volatile变量不可行,所有读写操作都需要最终的可执行文件)。尽管如此,我没有足够的时间阅读*(如完全摘要)标准的“原子”部分。 – 2011-05-13 07:45:21

相关问题