我想在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(cmd), std::ref(e2));
... //some other code
cmd = CMD1;
...
cmd = CMD2;
...
cmd = CMD1;
...
cmd = DONE;
wokerThread1.join();
wokerThread2.join();
return 0;
}
答案 0 :(得分:2)
这样做的一个原因是使用concurrent_bounded_queue
。您可以为此使用TBB's实现,也可以使用std::queue
和std::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));
std::thread wokerThread2 = 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,
CMD2,
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", std::this_thread::get_id(), 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", std::this_thread::get_id(), 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;
}
答案 1 :(得分:0)
正如您所提到的,您是一个c ++菜鸟,只需确保您没有犯任何这些错误“ https://www.acodersjourney.com/top-20-cplusplus-multithreading-mistakes/”
答案 2 :(得分:0)
您似乎可以在此处使用Observers
进行异步操作。这个想法是您的主控制线程为所有感兴趣的观察者更新CMD
,然后观察者根据CMD
执行特定的操作。在此示例中,我阻止了“更新”操作(在开始新作业之前,必须完成之前的作业)并返回void
。但是,您可能会考虑其他可能性,例如如果先前的操作仍在进行中,则返回false
。
#include <chrono>
#include <future>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
enum CMD
{
WAIT,
CMD1,
CMD2,
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::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);
}
}