线程保持运行和执行命令

我想在C ++中创建多线程,使其继续运行并等待主线程命令并相应地执行它们。这是我编写的代码,我知道它会导致spinning问题。所以问题是我如何让CPU停止运行工作线程,直到我更改命令。我知道有future and promise,但它们似乎不适合这种情况。

[edit]我是C ++新手,这太复杂了!如果有人可以与我共享一些教程或库来解决此问题,将不胜感激!

#include <iostream>
#include <thread>
#include <mutex>

class empty
{
public:
    empty() {}
    void work1()
    {
        std::cout << "work1" << std::endl;
    }
    void work2()
    {
        std::cout << "work2" << std::endl;
    }
};

enum CMD
{
    WAIT,CMD1,CMD2,DONE
};

void worker(CMD &cmd,empty &e)
{
    std::mutex mutex;
    while (cmd != DONE)
    {
        switch (cmd) {
            case WAIT:
                break;
            case CMD1:
                e.work1();      // excute cmd 1
                mutex.lock();
                cmd = WAIT;     // change cmd to WAIT
                mutex.unlock();
                break;
            case CMD2:
                e.work2();
                mutex.lock();
                cmd = WAIT;
                mutex.unlock();
                break;
            default:
                break;
        }
    }
}

int main(int argc,const char * argv[]) {

    empty e1 = empty();
    empty e2 = empty();

    CMD cmd = WAIT;

    // mutilple thread working on mutilple empty object
    std::thread wokerThread1 = std::thread(worker,std::ref(cmd),std::ref(e1));
    std::thread wokerThread2 = std::thread(worker,std::ref(e2));

    ... //some other code

    cmd = CMD1;

    ... 

    cmd = CMD2;

    ... 

    cmd = CMD1;

    ... 

    cmd = DONE;

    wokerThread1.join();
    wokerThread2.join();
    return 0;
}
rayest 回答:线程保持运行和执行命令

这样做的一个原因是使用concurrent_bounded_queue。您可以为此使用TBB's实现,也可以使用std::queuestd::condition_variable来实现它。

仅使用std的实现;

#include <queue>
#include <chrono>
#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

std::mutex g_m;
std::condition_variable g_cv;

enum CMD
{
  CMD1,CMD2,DONE
};

void push_cmd(std::queue<CMD>& tasks,CMD cmd) {
  const std::lock_guard<std::mutex> lock(g_m);
  tasks.push(cmd);
  g_cv.notify_one();
}

CMD pop_cmd(std::queue<CMD>& tasks) {
  std::unique_lock<std::mutex> lk(g_m);
  g_cv.wait(lk,[&tasks]{ return !tasks.empty(); });

  CMD cmd = tasks.front();
  tasks.pop();
  return cmd;
}

void execute_cmd(int cmd) {
  std::cout << std::this_thread::get_id() << ": cmd [" << cmd << "]" << std::endl;
  std::this_thread::sleep_for(std::chrono::seconds(cmd));
}

void worker(std::queue<CMD>& tasks) {
  CMD cmd = pop_cmd(tasks);
  while (true)
  {
    switch (cmd) {
      case CMD1:
        execute_cmd(1);
        break;
      case CMD2:
        execute_cmd(2);
        break;
      case DONE:
      default:
        return;
    }
  }
}

int main(int argc,const char * argv[]) {
  std::queue<CMD> tasks;

  std::thread wokerThread1 = std::thread(worker,std::ref(tasks));
  std::thread wokerThread2 = std::thread(worker,std::ref(tasks));

  push_cmd(tasks,CMD1);
  push_cmd(tasks,CMD2);

  // push `DONE` for each worker
  push_cmd(tasks,DONE);
  push_cmd(tasks,DONE);

  wokerThread1.join();
  wokerThread2.join();
  return 0;
}

使用tbb::concurrent_bounded_queue的实现;

#include <tbb/concurrent_queue.h>

void worker(tbb::concurrent_bounded_queue<CMD>& tasks) {
  while (true) {
    CMD cmd;
    tasks.pop(cmd);
    switch (cmd) {
    case CMD1:
      // excute cmd 1
      break;
    case CMD2:
      // excute cmd 2
      break;
    case DONE:
    default:
      return;
    }
  }
}

int main(int argc,const char * argv[]) {
  tbb::concurrent_bounded_queue<CMD> tasks;

  std::thread wokerThread1 = std::thread(worker,std::ref(tasks));

  ...
  tasks.push(CMD1);
  tasks.push(CMD2);
  ...
}

请注意,您想多次运行同一任务,可以创建一个Worker,将所有内容包装如下:

#include <chrono>
#include <queue>
#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

enum CMD
{
  CMD1,DONE
};

void executeCmd(int cmd) {
  printf("exec %u: cmd[%d]\n",std::this_thread::get_id(),cmd);
  std::this_thread::sleep_for(std::chrono::seconds(cmd));
}

class Worker
{
public:
  Worker()
    : _thread(std::thread(&Worker::work,this))
  {
  }

  void pushCmd(CMD cmd) {
    printf("push %u: cmd[%d]\n",cmd);
    const std::lock_guard<std::mutex> lock(_m);
    _tasks.push(cmd);
    _cv.notify_one();
  }

  void finish() {
    pushCmd(DONE);
    _thread.join();
  }

private:
  std::thread _thread;
  std::mutex _m;
  std::queue<CMD> _tasks;
  std::condition_variable _cv;

  CMD popCmd() {
    std::unique_lock<std::mutex> lk(_m);
    _cv.wait(lk,[&]{ return !_tasks.empty(); });

    CMD cmd = _tasks.front();
    printf("pop  %u: cmd[%d]\n",cmd);
    _tasks.pop();
    return cmd;
  }

  void work() {
    while (true) {
      CMD cmd = popCmd();
      switch (cmd) {
        case CMD1:
          executeCmd(1);
          break;
        case CMD2:
          executeCmd(2);
          break;
        case DONE:
        default:
          return;
      }
    }
  }
};

int main(int argc,const char * argv[]) {
  Worker w1,w2;
  w1.pushCmd(CMD1);
  w2.pushCmd(CMD1);

  w1.pushCmd(CMD2);
  w2.pushCmd(CMD2);

  w1.finish();
  w2.finish();

  return 0;
}
,

正如您所提到的,您是一个c ++菜鸟,只需确保您没有犯任何这些错误“ https://www.acodersjourney.com/top-20-cplusplus-multithreading-mistakes/

,

您似乎可以在此处使用Observers进行异步操作。这个想法是您的主控制线程为所有感兴趣的观察者更新CMD,然后观察者根据CMD执行特定的操作。在此示例中,我阻止了“更新”操作(在开始新作业之前,必须完成之前的作业)并返回void。但是,您可能会考虑其他可能性,例如如果先前的操作仍在进行中,则返回false

#include <chrono>
#include <future>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>

enum CMD
{
  WAIT,CMD1,DONE
};

class SomeSystem
{
public:
  SomeSystem() = default;
  void work1()
  {
    // let's pretend this work takes some time
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::cout << "work1" << std::endl;
  }
  void work2()
  {
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::cout << "work2" << std::endl;
  }
};

class CmdObserver
{
public:
  CmdObserver(std::shared_ptr<SomeSystem> system,int id): system_(system),id_(id)
  {
    std::cout << "observer[" << id_ << "] CTor" << std::endl;
  }
  void update(CMD cmd)
  {
    if (work_.valid() && work_.wait_for(std::chrono::seconds(0)) == std::future_status::timeout)
    {
      std::cout << "observer[" << id_ << "] blocking until previous work is finished" << std::endl;
    }
    work_ = std::async(std::launch::async,[this,cmd]() { doTheJob(cmd); });
  }

private:
  void doTheJob(CMD cmd)
  {
    std::cout << "observer[" << id_ << "] going to execute cmd " << cmd << std::endl;
    switch (cmd)
    {
      case CMD1: system_->work1(); break;
      case CMD2: system_->work2(); break;
      default: std::cout << cmd << std::endl;
    }
  }
  std::shared_ptr<SomeSystem> system_;
  // id_ is just for demonstration purposes
  int id_;
  std::future<void> work_;
};

int main()
{
  int observerId = 0;
  std::vector<std::shared_ptr<SomeSystem> > systems({
    std::make_shared<SomeSystem>(),std::make_shared<SomeSystem>(),std::make_shared<SomeSystem>()
  });
  std::vector<CmdObserver> observers;
  for (auto system : systems)
  {
    observers.push_back(CmdObserver(system,observerId));
    observerId++;
  }
  for (auto& observer : observers)
  {
    observer.update(CMD1);
  }
  for (auto& observer : observers)
  {
    observer.update(CMD2);
  }
  // let's pretend we do some long operation here
  std::this_thread::sleep_for(std::chrono::seconds(1));
  for (auto& observer : observers)
  {
    observer.update(CMD1);
  }
}
本文链接:https://www.f2er.com/3016263.html

大家都在问