【发布时间】:2018-08-08 08:36:31
【问题描述】:
我有以下代码:
class Foo {
private:
std::thread thread;
void run();
std::atomic_flag running;
std::thread::native_handle_type native;
public:
Foo(const std::string& filename);
virtual ~Foo();
virtual void doOnChange();
void start();
void quit();
};
#include "Foo.h"
#include <functional>
#include <iostream>
Foo::Foo(const std::string& filename) :
thread(), running(ATOMIC_FLAG_INIT) {
file = filename;
native = 0;
}
Foo::~Foo() {
quit();
}
void Foo::start() {
running.test_and_set();
try {
thread = std::thread(&Foo::run, this);
} catch (...) {
running.clear();
throw;
}
native = thread.native_handle();
}
void Foo::quit() {
running.clear();
pthread_cancel(native);
pthread_join(native, nullptr);
//c++11-style not working here
/*if (thread.joinable()) {
thread.join();
thread.detach();
}*/
}
void Foo::run() {
while (running.test_and_set()) {
numRead = read(fd, buf, BUF_LEN);
.....bla bla bla.......
}
}
我试图在我的程序清理代码中退出这个线程。使用 pthread 是可行的,但我想知道我是否可以仅使用 c++11 做一些更好的事情(没有本机句柄)。在我看来,使用 c++11 代码处理所有情况没有好方法。正如您在此处看到的,线程在读取系统调用时被阻塞。因此,即使我清除标志,线程仍将被阻塞,并且加入调用将永远阻塞。所以我真正需要的是一个中断(在这种情况下是pthread_cancel)。但是如果我调用pthread_cancel 我不能再调用c++11 join() 方法,因为它失败了,我只能调用pthread_join()。所以看来标准有一个很大的限制,我错过了什么吗?
编辑:
经过下面的讨论,我更改了 Foo 类实现,将 std::atomic_flag 替换为 std::atomic 并使用信号处理程序。我使用信号处理程序是因为我认为最好有一个通用的基类,在基类中使用自管道技巧太难了,逻辑应该委托给孩子。最终实现:
#include <thread>
#include <atomic>
class Foo {
private:
std::thread thread;
void mainFoo();
std::atomic<bool> running;
std::string name;
std::thread::native_handle_type native;
static void signalHandler(int signal);
void run();
public:
Thread(const std::string& name);
virtual ~Thread();
void start();
void quit();
void interrupt();
void join();
void detach();
const std::string& getName() const;
bool isRunning() const;
};
Cpp 文件:
#include <functional>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <Foo.h>
#include <csignal>
#include <iostream>
Foo::Foo(const std::string& name) :
name(name) {
running = false;
native = 0;
this->name.resize(16, '\0');
}
Foo::~Foo() {
}
void Foo::start() {
running = true;
try {
thread = std::thread(&Foo::mainFoo, this);
} catch (...) {
running = false;
throw;
}
native = thread.native_handle();
pthread_setname_np(native, name.c_str());
}
void Foo::quit() {
if (running) {
running = false;
pthread_kill(native, SIGINT);
if (thread.joinable()) {
thread.join();
}
}
}
void Foo::mainFoo() {
//enforce POSIX semantics
siginterrupt(SIGINT, true);
std::signal(SIGINT, signalHandler);
run();
running = false;
}
void Foo::join() {
if (thread.joinable())
thread.join();
}
void Foo::signalHandler(int signal) {
}
void Foo::interrupt() {
pthread_kill(native, SIGINT);
}
void Foo::detach() {
if (thread.joinable())
thread.detach();
}
const std::string& Foo::getName() const {
return name;
}
bool Foo::isRunning() const {
return running;
}
void Foo::run() {
while(isRunning()) {
num = read(.....);
//if read is interrupted loop again, this time
//isRunning() will return false
}
}
【问题讨论】:
-
强制“杀死”线程绝不是一个好主意,因为线程将无法释放它可能分配的任何资源。如果您需要能够要求一个线程在它结束之前退出,那么考虑使用例如非阻塞 I/O 或类似的。
-
在某些平台(MS Windows)中,终止线程会使您的应用程序处于不稳定状态。这由 MS 记录。简单示例:线程在终止时持有(内部)C++ 堆锁——现在你没有堆了。
-
@Some 程序员老兄实际上可以使用 pthread 调用清理处理程序,在我的示例中不是,但可能
-
还是不够。让我们以这个假设的情况为例:您使用
new在线程中的循环中分配对象。分配已成功,但尚未完成对指针的分配。然后线程被杀死,并且您泄漏对象(更不用说如果指针未初始化可能的UB)。除非您可以同步线程并杀死它,否则总会有泄漏和 UB 的机会。而且如果可以同步杀掉线程,为什么要一开始就强杀而不是让它自己清理呢? -
不要屏蔽。查看“readsome”、“in_avail”和相关函数。