在独立线程运行fswatch时Ctrl+C无法立即退出的原因解析
fswatch监听线程退出延迟问题分析
fswatch.hpp 来自GitHub的fswatch项目。
第一段测试代码
#include "fswatch.hpp" #include <iostream> #include <thread> void go() { auto watcher = fswatch("~", "/opt", "."); watcher.on(fswatch::Event::FILE_CREATED, [](auto &event) { std::cout << "File created: " << event.path << std::endl; }); watcher.on(fswatch::Event::FILE_MODIFIED, [](auto &event) { std::cout << "File modified: " << event.path << std::endl; }); watcher.on(fswatch::Event::FILE_DELETED, [](auto &event) { std::cout << "File deleted: " << event.path << std::endl; }); try { watcher.start(); } catch (std::filesystem::filesystem_error &error) { std::cout << error.what() << std::endl; } } int main() { std::thread t1(go); t1.join(); }
第二段测试代码
int main() { go(); }
问题
为何第一段代码按下Ctrl+C后程序需要等待很久才退出,而第二段代码可立即退出?
详细解释
核心原因在于SIGINT信号的投递目标和线程阻塞状态的差异:
信号投递规则:当按下Ctrl+C时,操作系统会向进程发送
SIGINT信号,默认情况下该信号只会递送到进程的主线程,而非子线程。第二段代码的执行逻辑:
go()在主线程中执行,watcher.start()会启动文件监听循环,内部依赖阻塞式系统调用(如inotify_read、kqueue等)等待文件事件。- 主线程收到
SIGINT后,阻塞的系统调用会被直接中断,监听循环随即退出,go()执行完毕,程序立即终止。
第一段代码的执行逻辑:
go()在子线程中执行,主线程卡在t1.join()调用上,等待子线程执行完成。- 子线程中的
watcher.start()同样依赖阻塞式系统调用,但它不会收到SIGINT信号(信号只给主线程),因此阻塞调用不会被中断,只能等待fswatch内部的超时机制触发(比如监听循环的轮询超时),子线程才会退出。 - 主线程必须等子线程完全退出后才能结束,因此会出现长时间等待的情况。
简单来说:第二段的监听循环在主线程,能被SIGINT中断;第一段的监听循环在子线程,收不到SIGINT,只能等超时,导致主线程迟迟无法完成join。
内容的提问来源于stack exchange,提问作者jack
相关产品推荐
相关产品推荐

