C++ 如何使用c++;11 std::thread来实现类似QThread的类?

C++ 如何使用c++;11 std::thread来实现类似QThread的类?,c++,multithreading,c++11,qthread,C++,Multithreading,C++11,Qthread,std::thread不是简单地由类继承,在销毁时不能自动加入,等等 很多陷阱,比如需要使用std::atomic\u bool停止,当使用std::thread作为成员变量来执行成员方法时,不能简单地共享对象的this 使用std::thread实现类似QThread的类有什么好的实践吗 我的目标是一个可继承的线程类,启用start(),detach(),stop()功能 例如,我写了一篇如下: #include <atomic> #include <chrono>

std::thread
不是简单地由类继承,在销毁时不能自动加入,等等

很多陷阱,比如需要使用
std::atomic\u bool
停止,当使用
std::thread
作为成员变量来执行成员方法时,不能简单地共享对象的
this

使用
std::thread
实现类似QThread的类有什么好的实践吗


我的目标是一个可继承的线程类,启用
start()
detach()
stop()
功能

例如,我写了一篇如下:

#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <thread>
#include <vector>

struct Thread {
  Thread(int id) {
    std::atomic_init(&(this->id), id);
    std::atomic_init(&(this->m_stop), false);
  }

  Thread(Thread &&rhs) :
    id(),
    m_stop(),
    m_thread(std::move(rhs.m_thread))
  {
    std::atomic_init(&(this->id), rhs.id.load());
    rhs.id.store(-1);
    std::atomic_init(&(this->m_stop), rhs.m_stop.load());
  }

  virtual ~Thread() {
    this->stop();
  }

  void start() {
    this->m_thread = std::move(std::thread(&Thread::work, this));
  }

  void stop() {
    this->m_stop.store(true);

    if (this->m_thread.joinable()) {
      this->m_thread.join();
    }
  }

  virtual void work() {
    while (!(this->m_stop)) {
      std::chrono::milliseconds ts(5000);
      std::this_thread::sleep_for(ts);
    }
  }

  std::atomic_int id;
  std::atomic_bool m_stop;
  std::thread m_thread;
};


int main() {
  srand(42);

  while (true) {
    std::vector<Thread> v;

    for (int i = 0; i < 10; ++i) {
      auto t = Thread(i);
      v.push_back(std::move(t));
      printf("Start %d\n", i);
      v[i].start();
    }
    printf("Start fin!\n");

    int time_sleep = rand() % 2000 + 1000;
    std::chrono::milliseconds ts(time_sleep);
    std::this_thread::sleep_for(ts);

    for (int i = 0; i < 10; ++i) {
      printf("Stop %d\n", i);
      v[i].stop();
      printf("Pop %d\n", i);
      v.pop_back();
    }
    printf("Stop fin!\n");
  }

  return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
结构线程{
线程(int-id){
std::原子初始化(&(this->id),id);
std::atomic_init(&(this->m_stop),false);
}
螺纹(螺纹和rhs):
id(),
m_stop(),
m_螺纹(标准::移动(rhs.m_螺纹))
{
std::atomic_init(&(this->id),rhs.id.load();
rhs.id.store(-1);
std::atomic_init(&(this->m_stop),rhs.m_stop.load();
}
虚拟线程(){
此->停止();
}
void start(){
this->m_thread=std::move(std::thread(&thread::work,this));
}
无效停止(){
此->m_stop.store(true);
如果(此->m_thread.joinable()){
此->m_thread.join();
}
}
虚空工作(){
而(!(此->m_站)){
标准时间:毫秒ts(5000);
std::this_线程::sleep_for(ts);
}
}
std::原子内部id;
std::原子波停止;
标准:螺纹m_螺纹;
};
int main(){
srand(42);
while(true){
std::向量v;
对于(int i=0;i<10;++i){
自动t=螺纹(i);
v、 推回(标准::移动(t));
printf(“开始%d\n”,i);
v[i].start();
}
printf(“启动fin!\n”);
int time_sleep=rand()%2000+1000;
std::chrono::毫秒ts(睡眠时间);
std::this_线程::sleep_for(ts);
对于(int i=0;i<10;++i){
printf(“停止%d\n”,i);
v[i].stop();
printf(“弹出%d\n”,i);
v、 向后弹出();
}
printf(“停止鳍!\n”);
}
返回0;
}

但是,在
stop0
它死锁之后,或者有时它的内核转储之后,我很难把它正确地处理好。

std::thread
是实现线程原语的最低构造块。因此,它没有提供像QThread那样丰富的接口,但与标准库中的同步原语一起,它允许您非常轻松地实现QThread提供的更复杂的行为

您正确地指出,从
std::thread
继承是一个坏主意(没有虚拟析构函数是一个致命的缺陷),我认为拥有多态线程类型一开始并不是最聪明的设计,但您可以轻松地将
std::thread
封装为任何类的成员(多态或非多态)如果你想的话

加入破坏行动实际上只是一项政策。将
std::thread
封装为成员的类可以简单地在其析构函数中调用
join
,有效地实现销毁时的自连接。这里不考虑并发性,因为根据定义,对象销毁总是非并发执行的。如果您想在多个(可能同时调用)执行路径之间共享线程的所有权,
std::shared_ptr
将为您处理该问题。但即使在这里,析构函数也总是由最后一个放弃其
共享的线程非并发执行

QThread::isInterruptionRequested
这样的东西可以用一个标志来实现,当然,对该标志的访问必须由类同步(使用互斥或使用原子标志)

更改线程的优先级不是由标准指定的,因为并非标准设想的所有平台都允许这样做,但是您可以使用特定于平台的代码自己实现这一点


等等。所有部件都在那里,您只需根据需要组装它们。

std::thread
是实现线程原语的最低构建块。因此,它没有提供像QThread那样丰富的接口,但与标准库中的同步原语一起,它允许您非常轻松地实现QThread提供的更复杂的行为

您正确地指出,从
std::thread
继承是一个坏主意(没有虚拟析构函数是一个致命的缺陷),我认为拥有多态线程类型一开始并不是最聪明的设计,但您可以轻松地将
std::thread
封装为任何类的成员(多态或非多态)如果你想的话

加入破坏行动实际上只是一项政策。将
std::thread
封装为成员的类可以简单地在其析构函数中调用
join
,有效地实现销毁时的自连接。这里不考虑并发性,因为根据定义,对象销毁总是非并发执行的。如果您想在多个(可能同时调用)执行路径之间共享线程的所有权,
std::shared_ptr
将为您处理该问题。但即使在这里,析构函数也总是由最后一个放弃其
共享的线程非并发执行

QThread::isInterruptionRequested
这样的东西可以用一个标志来实现,当然,对该标志的访问必须由类同步(使用互斥或使用原子标志)

更改线程的优先级不是由标准指定的,因为并非标准设想的所有平台都允许这样做,但是您可以使用特定于平台的代码自己实现这一点

等等。所有部件都在那里,您只需根据需要组装即可。

en