一步一步写线程之十六线程的安全退出之二例程
一、说明
在一篇分析了多线程的安全退出的相关机制和方式,那么本篇就针对前一篇的相关的分析进行举例分析。因为有些方法实现的方法类似,可能就不一一重复列举了,相关的例程主要以在Linux上的运行为主。
二、实例
线程间的同步,其实理解清楚动作的原理并不麻烦,麻烦的在于如何和业务较好的契和起来。直白的说就是用得恰到好处。所以下面的分析的方法,只是告诉大家这是一类手段,如何能更好的运用,才看开发者具体的要求是什么。
1、等待方式
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <thread>
#include <unistd.h>
std::atomic_bool quit = false;
struct Data {
int Display(int c) {
std::cout << "Display value:" << c << std::endl;
return c;
}
};
void threadWorkSleep(Data *d) {
//模拟工作
for (int c = 0; c < 10000; c++) {
std::cout << "threadWorkSleep:call Data func:" << d->Display(c) << std::endl;
}
}
int main() {
Data *pd = new Data;
std::thread t = std::thread(threadWorkSleep, pd);
t.detach();
// firt:sleep thread safe quit
sleep(1);
return 0;
}
大家可以试着调整一下等待和模拟工作的时间,就可以发现具体的关系。实际的场景下,可能要求必须完成线程的工作才能退出。而如果等待时长不够,则线程就来不及完成相关的工作就退出了,那么,就没有实现业务的要求。等待的方式很粗暴,但也很简单。
2、轮询方式
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <thread>
#include <unistd.h>
std::atomic_bool quit = false;
struct Data {
int Display(int c) {
std::cout << "Display value:" << c << std::endl;
return c;
}
};
void threadWorkPolling(Data *d) {
for (int c = 0; c < 10000; c++) {
std::cout << "threadWorkPolling:call Data func:" << d->Display(c) << std::endl;
}
quit = true;
}
int main() {
Data *pd = new Data;
// sec:Polling
std::thread tp = std::thread(threadWorkPolling, pd);
while (!quit) {
std::cout << "polling quit:" << quit << std::endl;
}
std::cout << "polling thread safe quit.quit is:" << quit << std::endl;
std::cout << "master thread thread!" << std::endl;
// or deatch
if (tp.joinable()) {
tp.join();
}
return 0;
}
轮询的方式其实就是不断反复的查看是否可以退出了,这样做虽然安全,但浪费时间。就和现实社会一样,本来一个人干得活还得安排一个人去没事转转。
3、消息方式
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <thread>
#include <unistd.h>
std::atomic_bool quit = false;
struct Data {
int Display(int c) {
std::cout << "Display value:" << c << std::endl;
return c;
}
};
// third:msg or signal
static void sigHandler(int sigNo) {
std::cout << "recv msg no is:" << sigNo << std::endl;
if (sigNo == SIGUSR1) {
quit = true;
std::cout << "recv SIGUSR1" << std::endl;
}
}
// third:msg or signal
void threadWorkMsg(Data *d) {
for (int c = 0; c < 10000; c++) {
std::cout << "threadWorkMsg:call Data func:" << d->Display(c) << std::endl;
}
int ret = raise(SIGUSR1);
if (ret < 0) {
std::cout << "SIGUSR1 msg send err!" << std::endl;
}
}
int main() {
Data *pd = new Data;
// msg
signal(SIGUSR1, sigHandler);
std::thread ts = std::thread(threadWorkMsg, pd);
ts.detach();
while (!quit) {
std::cout << "msg or signal quit:" << quit << std::endl;
}
std::cout << "polling thread safe quit.quit is:" << quit << std::endl;
return 0;
}
这个信号的例程因为和其它程序共用的原因,把信号放到了主程序这样看起来也有点轮询的意思,其实如果把事件接收放到线程中反而更好体现这种情况。有兴趣可以试试。
4、事件方式
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <thread>
#include <unistd.h>
std::atomic_bool quit = false;
struct Data {
int Display(int c) {
std::cout << "Display value:" << c << std::endl;
return c;
}
};
// fourth:event
std::condition_variable conv;
std::mutex mt;
bool signaled = false;
void threadWorkEvent(Data *d) {
for (int c = 0; c < 100; c++) {
std::cout << "threadWorkEvent:call Data func:" << d->Display(c) << std::endl;
}
signaled = true;
std::cout << "threadWorkEvent,set notify_one!" << std::endl;
conv.notify_one();
}
int main() {
Data *pd = new Data;
// event
std::thread te = std::thread(threadWorkEvent, pd);
te.detach();
std::unique_lock<std::mutex> lock(mt);
while (!signaled) {
std::cout << "thread start wait....!" << std::endl;
conv.wait(lock);
}
std::cout << "thread recv notify_one and quit wait!" << std::endl;
std::cout << "master thread thread!" << std::endl;
return 0;
}
其实这几个例程都非常简单,但可以一眼看明白几种手段的应用。可能老鸟儿们觉得没什么,但对于新手来说,可能还是非常有用的。其实真正复杂的在于线程结束时,相关的资源包括涉及到内存和IO等的处理。一个不小心这就出现各种问题。不过有了各个线程间互相协调的手段,就知道如何下手了。
三、总结
老生常谈的技术,可能对于不少开发者已经耳朵都听出茧子来了。可还是要说,为什么?这就和上学一样,你觉得你会了,而且你也明白了整个过程,甚至把作业都作得很好,可考试呢?大多数人仍然是一个中上游的水平。要是明白这个现象产生的道理,就明白现在这里说的什么道理。
熟能生巧,但很难产生思想!大家自己意会!