C++ C++;11:std::类内的线程,在构造函数中执行具有线程初始化的函数成员

C++ C++;11:std::类内的线程,在构造函数中执行具有线程初始化的函数成员,c++,multithreading,stl,c++11,C++,Multithreading,Stl,C++11,我正在尝试使用C++11中的std::thread。如果在一个执行其函数成员的类中可以有一个std::thread,我在任何地方都找不到。考虑下面的例子… 在我的try(下面)中,函数是run() 我使用gcc-4.4和-std=c++0x标志进行编译 #ifndef RUNNABLE_H #define RUNNABLE_H #include <thread> class Runnable { public: Runnable() : m_stop(fa

我正在尝试使用C++11中的std::thread。如果在一个执行其函数成员的类中可以有一个std::thread,我在任何地方都找不到。考虑下面的例子… 在我的try(下面)中,函数是run()

我使用gcc-4.4和-std=c++0x标志进行编译

#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
,对
run()
的调用可以调度到
Runnable::run()
,这是纯虚拟的,这反过来会导致未定义的行为


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

这里有一些代码需要仔细考虑:

#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
\ifndef RUNNABLE\u H
#定义可运行的
#包括
#包括
类可运行
{
公众:
Runnable():m_stop(),m_thread(){}
虚拟~Runnable(){try{stop();}catch(…){/*??*/}
Runnable(Runnable const&)=删除;
Runnable&operator=(Runnable const&)=delete;
void stop(){m_stop=true;m_thread.join();}
void start(){m_thread=std::thread(&Runnable::run,this);}
受保护的:
虚空运行()=0;
标准::原子m_停止;
私人:
标准:螺纹m_螺纹;
};
类myThread:public Runnable
{
受保护的:
void run(){while(!m_stop){/*做点什么…*/};}
};
#endif//RUNNABLE\u H

一些注意事项:

  • 像你那样简单地宣称“停止”是远远不够的;阅读记忆障碍
  • std::thread::join
    可以抛出,因此从析构函数调用它而不使用
    try..catch
    是不计后果的
  • std::thread
    std::atomic
    是不可复制的,因此如果不是为了避免使用VC发出C4512警告,则应将
    Runnable
    标记为不可复制++

这就是为什么需要一个
start()
函数来完成实际的启动工作。我不认为线程花费足够长的时间来调度会改变行为,它的构造函数不是会复制(从而切片)bind的结果吗(&Runnable::run,this)?@Etienne de Martel:有不同的方法,
start()
方法选项显然就是其中之一。另一种是boost和c++0x所采用的方法:将runnable对象与thread对象分离,这将使操作作为构造函数的参数运行。这确保了将要执行的对象是完全构造的。当然,也就是说,如果你不强行进入一个破坏它的框架…@Cubbi:
这个
是一个
可运行的*
,它被复制了——指针——但指针对象(
*这个
)没有被复制。基本上,您是将指向尚未完全构造的对象的指针传递给线程。指针的传递不是问题(标准保证您可以传递指针),但是取消引用它是UB。啊,是的。我只是习惯于引用所有传递给线程的无法复制的内容。我不认为
m\u stop
变量需要是
volatile
std::atomic
将解决多线程问题,而
volatile
在这方面没有任何额外的帮助。@David Rodríguez:《联邦存款保险条例》第29.6.5/3条明确指出了这一点。如果std::atomic对象不是易失性的,那么对该对象的访问可能会被重新排序;std::atomic类型没有什么特别的地方可以改变这一事实。(由于无法编辑的拼写错误而重新粘贴)我没有注意到前面评论中的拼写错误,但我认为它比新版本更精确。标准在该段中说,原子对象上的操作可以合并(不说重新排序)。我的理解是,
std::atomic i=0;对于(;i<10;i=i+1){}
可以转换为
std::atomic i=10(这对于
volatile
变量是不可行的,因为在最终可执行文件中需要所有读写操作)。不过,我没有足够的时间阅读(如在《全面文摘》中)标准的
atomic
部分。-1:
std::atomic
(无锁)和
volatile
(硬件)是正交概念。请看我的问题,特别是@Herb Sutter的答案。这里不需要Volatile,
std::atomic
的语义足以防止有害的重新排序。不过,答案中的其他所有内容都很好。我正处于圣战中,反对<代码>在我的日常工作中基于Value</代码>的并发。为什么<代码> *这个< /C> >在<代码> STD:::线程(& Runn::Run,*this)< /Cuth>构造函数?不应该只是
这个
指针吗?因此线程访问相同的对象和bool属性?您可能希望在stop()函数中将m_stop设置为true。。。。
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’|
#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