C++守护进程无限间隔循环优化咨询:CPU占用过高及代码等价性验证
问题:守护进程定时任务CPU占用过高及代码等价性疑问
我正在开发一个以守护进程(daemon)运行的应用程序,需按固定间隔执行代码,当前代码如下:
#include <iostream> #include <thread> using namespace std; int main() { thread([=]() { while (true) { try { cout << "log" << endl; this_thread::sleep_for(chrono::milliseconds(3000)); } catch (...) { cout << "Some errors here :/" << endl; } } }).detach(); while (true); }
通过top命令观察到该程序CPU占用率约80%,我担心代码并非最优实现,请问该如何优化?另外,我的这段代码是否与StackOverflow某篇回答中的代码等价?
解答
一、CPU占用过高的优化方案
兄弟,你这CPU飙高的核心原因太明显了——主函数里的while(true);是个空循环!CPU会拼尽全力一直执行这个空循环,完全得不到休息,占用率自然居高不下。
优化的思路很简单,让主线程不要空转,而是进入等待状态,具体有几种方案:
方案1:让主线程进入休眠(最简单)
把主函数的空循环替换成无限休眠,这样主线程不会占用CPU资源:
#include <iostream> #include <thread> #include <chrono> using namespace std; int main() { thread([=]() { while (true) { try { cout << "log" << endl; this_thread::sleep_for(chrono::milliseconds(3000)); } catch (...) { cout << "Some errors here :/" << endl; } } }).detach(); // 替换空循环,让主线程无限休眠 while (true) { this_thread::sleep_for(chrono::hours(1)); } }
方案2:用条件变量让主线程等待(更优雅)
如果后续需要给主线程添加退出逻辑,用条件变量会更灵活:
#include <iostream> #include <thread> #include <chrono> #include <mutex> #include <condition_variable> using namespace std; mutex mtx; condition_variable cv; bool should_exit = false; int main() { thread([=]() { while (!should_exit) { try { cout << "log" << endl; this_thread::sleep_for(chrono::milliseconds(3000)); } catch (...) { cout << "Some errors here :/" << endl; } } }).detach(); // 主线程等待条件变量,直到收到退出信号 unique_lock<mutex> lock(mtx); cv.wait(lock, []{ return should_exit; }); }
方案3:实现真正的守护进程(符合你的需求)
你提到要做守护进程,但当前代码根本不是真正的daemon——它还和终端关联,没有脱离会话,也没有处理后台运行的必要步骤。真正的daemon需要完成这些操作:
- 调用
fork()创建子进程,父进程退出 - 子进程调用
setsid()创建新会话,脱离终端 - 再次
fork()避免成为会话组长 - 切换工作目录到
/,避免占用挂载点 - 关闭所有文件描述符,重定向标准输入输出到
/dev/null
结合定时任务的完整daemon示例大概是这样:
#include <iostream> #include <thread> #include <chrono> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> void daemonize() { // 第一步:fork子进程,父进程退出 pid_t pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) exit(EXIT_SUCCESS); // 第二步:创建新会话,脱离终端 if (setsid() < 0) exit(EXIT_FAILURE); // 第三步:再次fork,避免成为会话组长,防止重新打开终端 pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) exit(EXIT_SUCCESS); // 第四步:切换工作目录到根目录 chdir("/"); // 第五步:设置文件权限掩码 umask(0); // 第六步:关闭所有文件描述符 for (int i = sysconf(_SC_OPEN_MAX); i >= 0; --i) { close(i); } // 第七步:重定向标准输入输出到/dev/null open("/dev/null", O_RDONLY); open("/dev/null", O_WRONLY); open("/dev/null", O_RDWR); } int main() { // 先把程序转成真正的守护进程 daemonize(); // 执行定时任务 while (true) { try { // 这里写你的定时逻辑,比如写入日志文件 int fd = open("/var/log/daemon_example.log", O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd >= 0) { const char* log = "daemon running\n"; write(fd, log, strlen(log)); close(fd); } std::this_thread::sleep_for(std::chrono::milliseconds(3000)); } catch (...) { // 错误处理,同样写入日志 int fd = open("/var/log/daemon_example.log", O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd >= 0) { const char* err = "some error occurred\n"; write(fd, err, strlen(err)); close(fd); } std::this_thread::sleep_for(std::chrono::milliseconds(3000)); } } }
二、与目标代码的等价性对比
完全不等价!那篇回答的代码是标准的守护进程创建流程,包含了我上面提到的fork、setsid、关闭文件描述符等核心步骤,目的是让程序脱离终端,在后台独立运行,成为真正的daemon。
而你的代码只是在主进程里创建了一个detach的后台线程,然后主进程空循环——这本质上还是一个前台程序,只要终端关闭,你的程序大概率会跟着退出,完全没有实现daemon的核心特性,和那篇回答的代码差得很远。
内容的提问来源于stack exchange,提问作者sal-k
相关产品推荐
相关产品推荐

