带定时器的 C++ 多线程编程
Posted
技术标签:
【中文标题】带定时器的 C++ 多线程编程【英文标题】:C++ multi thread programming with timer 【发布时间】:2016-03-17 20:06:33 【问题描述】:我是多线程编程的新手,所以这个问题可能看起来有点傻,但我真的需要解决这个问题,这样我才能将它应用到我的项目中(这要复杂得多)。 下面是我的代码,我试图让 2 个线程(父线程和子线程)在它们执行时更新同一个共享定时器,并在定时器达到特定限制时停止。 但是当我编译并执行这段代码时,有两种不同的结果: 1. child 打印“由 child 在 200000 完成”但程序没有退出; 2.child打印“done by child at 200000”并退出后,parent继续执行,打印几十行“parent doing work”和“parent at 190000”,然后打印“done by parent at 200000”和程序正确退出。 我想要的行为是对于更新计时器的任何线程,达到限制并退出,另一个线程也应该停止执行并退出。我想我可能在这里遗漏了一些微不足道的东西,但我已经尝试以多种方式更改代码,但我尝试的任何方法似乎都不起作用。任何帮助将不胜感激:)
#include <iostream>
#include <unistd.h>
#include <mutex>
#include <time.h>
using namespace std;
mutex mtx;
int main ()
int rc;
volatile int done = 0;
clock_t start = clock();
volatile clock_t now;
rc = fork();
if (rc == 0) //child
while (true)
cout << "child doing work" << endl;
mtx.lock();
now = clock() - start;
if (done)
mtx.unlock();
break;
if (now >= 200000 && !done)
done = 1;
cout << "done by child at " << now << endl;
mtx.unlock();
break;
cout << "child at " << now << endl;
mtx.unlock();
_exit(0);
else // parent
while (true)
cout << "parent doing work" << endl;
mtx.lock();
now = clock() - start;
if (done)
mtx.unlock();
break;
if (now >= 200000 && !done)
done = 1;
cout << "done by parent at " << now << endl;
mtx.unlock();
break;
cout << "parent at " << now << endl;
mtx.unlock();
return 0;
【问题讨论】:
volatile
没有做你认为它使用 std::atomic 的事情。而且你不是在测试线程而是进程 - fork
在你的进程中创建一个新进程而不是一个新线程。
你没有两个线程,而是一个父进程和一个子进程(那是完全不同的)。
请停止使用volatile
。对于fork
和进程,您可以阅读beej.us/guide/bgipc for IPC。
那些不是独立的线程,它们是独立的进程。
@LongLin 为什么不用std::thread
?
【参考方案1】:
多进程
您的代码是多进程而不是多线程:fork()
将通过复制调用进程来创建一个新的单独进程。
结果:在复制的那一刻,所有变量在两个过程中都包含相同的值。但是每个进程都有自己的副本,因此在父进程中修改的变量不会在子进程的地址空间中更新,反之亦然。
如果你想在进程之间共享变量,你应该看看this SO question
多线程
对于真正的多线程,你应该使用std::thread
。忘记 volatile,因为它不是线程安全的。请改用<atomic>
,如awesome video 中所述。
这里是第一次尝试:
#include <iostream>
#include <mutex>
#include <thread>
#include <atomic>
#include <time.h>
using namespace std;
void child (atomic<int>& done, atomic<clock_t>& now, clock_t start)
while (!done)
cout << "child doing work" << endl;
now = clock() - start;
if (now >= 2000 && !done)
done = 1;
cout << "done by child at " << now << endl;
cout << "child at " << now << endl;
this_thread::yield();
void parent (atomic<int>& done, atomic<clock_t>& now, clock_t start)
while (!done)
cout << "parent doing work" << endl;
now = clock() - start;
if (now >= 2000 && !done)
done = 1;
cout << "done by parent at " << now << endl;
cout << "parent at " << now << endl;
this_thread::yield();
int main ()
atomic<int> done0;
clock_t start = clock();
atomic<clock_t> now;
thread t(child, std::ref(done), std::ref(now), start); // attention, without ref, you get clones
parent (done, now, start);
t.join();
return 0;
请注意,您不需要使用互斥锁来保护原子访问,如果您想这样做,建议使用 lock_guard
替代。
这个例子当然比较弱,因为如果你测试一个原子变量 if 条件,它的值可能在进入 if 块时已经改变了。这不会导致您的逻辑出现问题,其中“完成”表示“完成”。但如果您需要更谨慎的方法,compare_exchange_weak()
或 compare_exchange_strong()
可以提供更多帮助。
【讨论】:
感谢您的示例。在阅读上述 cmets 后不久,我确实让它工作了,我记得发表评论说。无论如何,我有一个后续问题。我的解决方案与您的解决方案非常相似。起初,在 main() 中,我将 parent 行放在 child 行的前面,输出表明 parent 总是先执行,而 child 只有在 parent 完成后才能执行;然后我交换了这两条线,它按预期工作。那么,围绕这两条线交换是否真的会影响我提到的执行顺序?如果是这样,为什么?在此先感谢:) 你总是至少有一个线程在执行:主线程。它按顺序执行语句,因此如果遇到对 parent 的调用,它将执行该函数调用。只有当您创建并启动一个线程对象时,才会并行执行第二个线程(尽可能)。因此,如果您创建此对象,则首先要在第二个线程启动时父级必须结束。主线程将礼貌地等待连接发生。以其他顺序启动第二个线程并在主线程执行父线程时执行子线程。 我的评论是日志,但尝试用它制作一个序列图,我认为它会变得更清晰以上是关于带定时器的 C++ 多线程编程的主要内容,如果未能解决你的问题,请参考以下文章