通过安装信号处理程序关闭多线程应用程序
Shutting down a multithreaded application by installing a signal handler
在下面的代码中,我创建了一个玩具类,该类具有一个线程,该线程写入队列,而另一个线程从该队列读取并将其打印到stdout
。现在,为了干净地关闭系统,我为SIGINT
设置了一个处理程序。我希望信号处理程序设置std::atomic<bool>
变量stopFlag
,这将导致threadB
将毒丸(哨兵(推到遇到threadA
将停止的队列上。
class TestClass
{
public:
TestClass();
~TestClass();
void shutDown();
TestClass(const TestClass&) = delete;
TestClass& operator=(const TestClass&) = delete;
private:
void init();
void postResults();
std::string getResult();
void processResults();
std::atomic<bool> stopFlag;
std::mutex outQueueMutex;
std::condition_variable outQueueConditionVariable;
std::queue<std::string> outQueue;
std::unique_ptr<std::thread> threadA;
std::unique_ptr<std::thread> threadB;
};
void TestClass::init()
{
threadA = std::make_unique<std::thread>(&TestClass::processResults, std::ref(*this));
threadB = std::make_unique<std::thread>(&TestClass::postResults, std::ref(*this));
}
TestClass::TestClass():
stopFlag(false)
{
init();
}
TestClass::~TestClass()
{
threadB->join();
}
void TestClass::postResults()
{
while(true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::string name = "ABCDEF";
{
std::unique_lock<std::mutex> lock(outQueueMutex);
outQueue.push(name);
outQueueConditionVariable.notify_one();
}
if(stopFlag)
{
/*For shutting down output thread*/
auto poisonPill = std::string();
{
std::unique_lock<std::mutex> lock(outQueueMutex);
outQueue.push(poisonPill);
outQueueConditionVariable.notify_one();
}
threadA->join();
break;
}
}
}
void TestClass::shutDown()
{
stopFlag = true;
}
std::string TestClass::getResult()
{
std::string result;
{
std::unique_lock<std::mutex> lock(outQueueMutex);
while(outQueue.empty())
{
outQueueConditionVariable.wait(lock);
}
result= outQueue.front();
outQueue.pop();
}
return result;
}
void TestClass::processResults()
{
while(true)
{
const auto result = getResult();
if(result.empty())
{
break;
}
std::cout << result << std::endl;
}
}
static void sigIntHandler(std::shared_ptr<TestClass> t, int)
{
t->shutDown();
}
static std::function<void(int)> handler;
int main()
{
auto testClass = std::make_shared<TestClass>();
handler = std::bind(sigIntHandler, testClass, std::placeholders::_1);
std::signal(SIGINT, [](int n){ handler(n);});
return 0;
}
我使用 gcc 5.2 使用 -std=c++14 标志编译了它。在我的 CentOS 7 机器上按 Ctrl-C 时,我收到以下错误,
terminate called after throwing an instance of 'std::system_error'
what(): Invalid argument
Aborted (core dumped)
请帮助我了解发生了什么。
发生的情况是,您的main
函数立即退出,销毁全局handler
对象,然后testClass
。然后主线程在TestClass::~TestClass
中被阻塞。信号处理程序最终访问已经销毁的对象,这会导致未定义的行为。
根本原因是由于共享指针而导致的对象所有权未定义 - 您不知道最终会破坏对象的内容和时间。
更通用的方法是使用另一个线程来处理所有信号并阻止所有其他线程中的信号。然后,该信号处理线程可以在接收到信号时调用任何函数。
这里也根本不需要智能指针和函数包装器。
例:
class TestClass
{
public:
TestClass();
~TestClass();
void shutDown();
TestClass(const TestClass&) = delete;
TestClass& operator=(const TestClass&) = delete;
private:
void postResults();
std::string getResult();
void processResults();
std::mutex outQueueMutex;
std::condition_variable outQueueConditionVariable;
std::queue<std::string> outQueue;
bool stop = false;
std::thread threadA;
std::thread threadB;
};
TestClass::TestClass()
: threadA(std::thread(&TestClass::processResults, this))
, threadB(std::thread(&TestClass::postResults, this))
{}
TestClass::~TestClass() {
threadA.join();
threadB.join();
}
void TestClass::postResults() {
while(true) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::string name = "ABCDEF";
{
std::unique_lock<std::mutex> lock(outQueueMutex);
if(stop)
return;
outQueue.push(name);
outQueueConditionVariable.notify_one();
}
}
}
void TestClass::shutDown() {
std::unique_lock<std::mutex> lock(outQueueMutex);
stop = true;
outQueueConditionVariable.notify_one();
}
std::string TestClass::getResult() {
std::string result;
{
std::unique_lock<std::mutex> lock(outQueueMutex);
while(!stop && outQueue.empty())
outQueueConditionVariable.wait(lock);
if(stop)
return result;
result= outQueue.front();
outQueue.pop();
}
return result;
}
void TestClass::processResults()
{
while(true) {
const auto result = getResult();
if(result.empty())
break;
std::cout << result << std::endl;
}
}
int main() {
// Block signals in all threads.
sigset_t sigset;
sigfillset(&sigset);
::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
TestClass testClass;
std::thread signal_thread([&testClass]() {
// Unblock signals in this thread only.
sigset_t sigset;
sigfillset(&sigset);
int signo = ::sigwaitinfo(&sigset, nullptr);
if(-1 == signo)
std::abort();
std::cout << "Received signal " << signo << 'n';
testClass.shutDown();
});
signal_thread.join();
}
在您的平台上,当出现真正的SIGINT
信号时,将调用此信号处理程序。可以在此信号处理程序中调用的函数列表相当有限,调用任何其他函数都会导致未定义的行为。
相关文章:
- 通过安装信号处理程序关闭多线程应用程序
- 将数组作为多线程应用程序中函数的返回传递
- 修改多线程应用程序中的对象
- C++多线程应用程序将永远挂起
- 具有多线程应用程序的 Nanomsg 无阻塞双向套接字
- 在接收 SIGINT 操作时适当地关闭多线程 c++ 应用程序
- 如何在多线程应用程序中获得花费的时间
- Linux VM(重型多线程应用程序)的性能改进
- 在C 多线程应用程序中,设置了并发线程的最大数量
- C++多线程应用程序崩溃
- 多线程应用程序中的零MQ处理中断
- 使用C 的多线程应用程序中测量全局时间(壁挂)的最快方法
- 如何在多线程应用程序中使用 pybind11
- 多线程应用程序中的独立日志记录
- 设计一个扩展良好的多线程应用程序
- 在 Linux 上为 Windows 编译多线程应用程序 [ C++ ]
- 如何为多线程应用程序创建全局对象
- 管理多线程应用程序中的共享变量
- 如何在C++中创建多线程应用程序
- 使用来自多线程应用程序的ptrace