C++并发编程之Thread创建和退出
1、线程的基本概念
线程的基本概念,我们看下定义:
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程,但轻量进程更多指内核线程,而把用户线程称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈,寄存器环境,自己的线程本地存储。
一个进程可以有很多线程,每条线程并行执行不同的任务。
在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
(1)轻型实体
线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。TCB包括以下信息:
(1)线程状态。
(2)当线程不运行时,被保存的现场资源。
(3)一组执行堆栈。
(4)存放每个线程的局部变量主存区。
(5)访问同一个进程中的主存和其它资源。
用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
(2)独立调度和分派的基本单位。
在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
(3)可并发执行。
在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。
(4)共享进程资源。
在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
用人话说就是:从前一个人从头干到尾的工作,现在拿两个人去做,给同样的设备、工资和条件。但是呢,这个新来的人听老员工的安排。
2、thread的定义&创建
thread源代码定义如下:
class thread
{public:class id;typedef void *native_handle_type;thread() noexcept;template<class _Fn,class... _Args,class = enable_if_t<!is_same_v<remove_cv_t<remove_reference_t<_Fn>>, thread>>>explicit thread(_Fn&& _Fx, _Args&&... _Ax){ // construct with _Fx(_Ax...)_Launch(&_Thr,_STD make_unique<tuple<decay_t<_Fn>, decay_t<_Args>...> >(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...));}~thread() noexcept;thread(thread&& _Other) noexcept: _Thr(_Other._Thr);thread& operator=(thread&& _Other) noexcept;thread(const thread&) = delete;thread& operator=(const thread&) = delete;void swap(thread& _Other) noexcept;_NODISCARD bool joinable() const noexcept;void join();void detach();_NODISCARD id get_id() const noexcept;_NODISCARD static unsigned int hardware_concurrency() noexcept;_NODISCARD native_handle_type native_handle();
private:thread& _Move_thread(thread& _Other);_Thrd_t _Thr;
};
std::thread
default thread() noecept; //默认的构造函数,什么都没有,即创建一个新的空的thread执行对象。
initialization template <class Fn,class ... Args>
explicit thread(Fn&& fn,Args&&... args);//构造模板,设置执行的函数,并且传入多个参数。
copy[deleted] thread(const thread&) = delete;//拷贝构造函数=delete,也就是说 thread 不支持拷贝构造。
move() thread(thread&& x) noexcept;//move操作,即用新的thread代替x的所有功能。
具体的创建线程示例如下:
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <sys/types.h>
#include <unistd.h>using namespace std;void add_1(int n)
{std::cout << "Enter "<< __function__ << "thread..."<< std::endl;for(int i = 0;i < 5;i++){std::this_thread::sleep_for(std::chrono::milliseconds(10));}std::cout << "Exit "<< __function__ << "thread..."<< std::endl;
}
void add_2(int ¶m)
{std::cout << "Enter "<< __function__ << "thread..."<< std::endl;for(int i = 0;i < 10;i ++) {++param;std::this_thread::sleep_for(std::chrono::milliseconds(10));}std::cout << "Exit "<< __function__ << "thread..."<< std::endl;
}int main()
{int n = 0;std::thread test_t1; //创建一个新的空的thread执行对象。std::thread test_t2(add_1,n+1);//创建thread执行对象,并传递参数n+1。std::thread test_t3(add_2,std::ref(n));//创建thread执行对象,并传递参数n。std::thread test_t4(std::move(test_t3));//move操作,用test_t4代替test_t3进行线程的操作。test_t2.join();test_t4.join();std::cout<< "N = "<<n<<std::endl;
}
当然了,是可以使用lamb、类函数、function模板的,具体的示例如下:
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <sys/types.h>
#include <unistd.h>using namespace std;//函数
void func_add(int parama,int paramb,int ¶mc)
{std::cout << __func__<<std::endl;paramc = paramc + parama + paramb;std::cout << __func__ << " paramc = "<< paramc<<std::endl;
}
//类
class Add
{public:Add(){}~Add(){}void Add_add(int parama,int paramb,int ¶mc){std::cout << __func__<<std::endl;paramc = paramc + parama + paramb;std::cout << __func__ << " paramc = "<< paramc<<std::endl;}static void static_add(int parama,int paramb,int ¶mc){std::cout << __func__<<std::endl;paramc = paramc + parama + paramb;std::cout << __func__ << " paramc = "<< paramc <<std::endl;}
};//lambd函数对象
auto lamb_add = [](int parama,int paramb,int ¶mc) {std::cout << __func__<<std::endl; paramc = paramc + parama + paramb; std::cout << __func__ << " paramc = "<< paramc<<std::endl;};//function
std::function <void(int, int,int &)> func_test = func_add;int main()
{int a = 10;int b = 20;int c = 0;Add test_add;std::thread thread1(func_add, a, b, std::ref(c));if (thread1.joinable())thread1.join();std::cout << "函数func_add c = "<< c << std::endl;std::thread thread2(Add::static_add, a, b, std::ref(c));if (thread2.joinable())thread2.join();std::cout << "类的static add c = "<< c << std::endl;std::thread thread3(&Add::Add_add,&test_add, a, b,std::ref(c));if (thread3.joinable())thread3.join();std::cout << "类的Add_add c = "<< c << std::endl;std::thread thread4(lamb_add, a, b, std::ref(c));if (thread4.joinable())thread4.join();std::cout << "lamb_add c = "<< c << std::endl;std::thread thread5(func_test,a,b,std::ref(c));if (thread5.joinable())thread5.join();std::cout << "function c = "<< c << std::endl;return 0;
}
//OUT
//func_add
//func_add paramc = 30
//函数func_add c = 30
//static_add
//static_add paramc = 60
//类的static add c = 60
//Add_add
//Add_add paramc = 90
//类的Add_add c = 90
//operator()
//operator() paramc = 120
//lamb_add c = 120
//func_add
//func_add paramc = 150
//function c = 150
3、join、joinable、detach函数介绍
thread::join()的功能是,阻塞当前调用的线程,知道其标识的线程结束。在join()函数执行期间会清理子线程的内存和资源空间。这个时候主线程会一直阻塞在调用的地方。直到join()函数返回,
thread::joinable()函数用于判断主线程和子线程的连接状态,即返回true时,主线程和子线程是存在关联关系的,返回false时,主线程和子线程不存在关联关系。
thread::detach()函数是分离执行的线程。允许子线程独立运行,即子线程可以脱离主线程运行,但是其运行结束后子线程会自动释放自己所占有的系统资源,不需要主线程主动释放。非常的银杏。但是存在一种情况,就是主线程退出后,会一同把其创建的子线程也销毁掉。这点是需要注意的。
具体的使用方法如下(主要是关注打印顺序):
/*************************************************************************> File Name: thread_exit.cpp> Author: 小和尚敲木鱼> Mail: > Created Time: Wed 22 Sep 2021 07:46:50 AM PDT************************************************************************/
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
/*****************************文件说明************************************ 1、线程退出 join() detach()* 2、joinable()
***********************************************************************/
void handlesomething1()
{std::cout << "[" << __func__ << "]" << " doing some thing"<< std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "[" << __func__ << "]" << "done..." << std::endl;
}
void handlesomething2()
{std::cout << "[" << __func__ << "]" << " doing some thing"<< std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "[" << __func__ << "]" << "done..." << std::endl;
}
//
/*
int main(int agc,char * agv[])
{std::cout << "main thread begin"<< std::endl;std::thread thread1(handlesomething1);std::thread thread2(handlesomething2);thread1.join();thread2.join();std::cout << "main thread waiting... exit" << std::endl;return 0;
}*/
//OUT
//main thread begin
//[handlesomething2] doing some thing
//[handlesomething1] doing some thing
//[handlesomething2]done...
//[handlesomething1]done...
//main thread waiting... exit
////
/*
int main(int agc,char * agv[])
{std::cout << "main thread begin"<< std::endl;std::thread thread1(handlesomething1);std::thread thread2(handlesomething2);thread1.detach();thread2.detach();std::cout << "main thread waiting... exit" << std::endl;std::this_thread::sleep_for(std::chrono::seconds(3));return 0;
}*/
//OUT
//main thread begin
//[handlesomething1] doing some thing
//main thread waiting... exit
//[handlesomething2] doing some thing
//[handlesomething1]done...
//[handlesomething2]done...
//
4、operrator=介绍
线程时不可直接复制的,但是支持移动。
//转移
thread& operator=(thread&& _Other) noexcept;
thread(const thread&) = delete;
//拷贝
thread& operator=(const thread&) = delete;
通过上面的定义,可以看到other是一个右值的话,则会进行资源的所有权转移。但是如果other不是一个右值,则被禁止。
5、pthread exit
5.1 return
线程函数的return返回。return返回这种方式是最安全的,在返回后,会调用_endthreadex()清理资源(_beginthreadex申请的资源)。同时也要保证申请的对象内存释放。
5.2 _endthreadex() & ExitThread()
使用这两个API退出线程,则不会执行线程的return返回,不能保证线程内申请的类对象完全释放,容易造成内存泄漏。
5.3 TerminateThread()
TerminateThread原型如下:
BOOL TerminateThread(
HANDLE hThread,
DWORD dwExitCode);
TerminateThread能够撤消任何线程。hThread参数用于标识被终止运行的线程的句柄。这是windows下停止线程的API函数。当线程终止运行时,它的退出代码成为你作为dwExitCode参数传递的值。同时,线程的内核对象的使用计数也被递减。注意TerminateThread函数是异步运行的函数,其是通知系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
5.4 进程退出
进程退出适用于,当前进程只有一个线程,可以采用进程退出的方式直接退出线程。
进程退出:
1.return返回退出
在main函数中执行return可以终止进程,并将控制权交给调用函数。一般原则程序执行正常退出return 0,而执行函数出错退出return -1。
2.使用exit退出
exit()是一个标准C库函数,执行exit()会进行一些清理工作,最后调用_exit()函数。
函数原型:
void exit(int status);
3.调用_exit()和_Exit()
5.5 pthread_cancel
pthread_cancel的头文件:
<pthread.h>
int pthread_cancel(pthread_t thread);
主线程持有子线程的线程ID,通过函数pthread_cancel发送cancle信号,成功返回0,否则失败描述符。对于没有目标线程也会导致信号发失败(返回信号 ESRCH)。pthread_cancel只是向目标线程发送cancle信号,至于目标线程如何处理并且怎么结束是其自己控制的。注意的是,发送了取消信号之后,不会立即取消,得等到子线程下次调起了之后再自己执行退出。
6、总结
线程的创建和控制还是比较简单,主要是注意点是线程创建时的参数传入时,引用需要使用std::ref()函数。其余的线程相关函数后续补充。
C++并发编程之Thread创建和退出相关推荐
- Java并发编程之Thread类详解
Thread类详解: 线程(Thread): 是操作系统进行调度的最小单位,Java中的线程是对操作系统线程的封装.本文从线程的创建到停止结合代码和具体实例分析一下关于java线程的一些常见问题. 线 ...
- c++并发编程之thread::join()和thread::detach()(转)
thread::join(): 阻塞当前线程,直至 *this 所标识的线程完成其执行.*this 所标识的线程的完成同步于从 join() 的成功返回. 该方法简单暴力,主线程等待子进程期间什么都不 ...
- java并发编程之Thread.sleep方法详解
Thread.sleep方法的作用: 使当前线程暂停执行一段时间,交出cpu的执行时间片,并且在暂停期间不会参与cpu时间片的获取.直到等待时间结束恢复到就绪状态,是否执行还要看OS的调度,或者在这段 ...
- java并发编程之thread.join()方法详解
thread.join()方法的作用:保证线程的执行结果的可见性.原理是通过阻塞主线程实现的. 代码Demo如下: public class ThreadJoinDemo {public static ...
- zbb20180929 thread java并发编程之Condition
java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...
- Python并发编程之threading模块
Python并发编程之threading模块 threading 模块 1. Timer对象 2. Lock对象 3. RLock 4. 信号量和有边界的信号量 5. 事件 6. 条件变量 7. 使用 ...
- 并发编程之 Executor 线程池原理与源码解读
并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...
- JUC 高并发编程之JUC三大辅助类
JUC 高并发编程之JUC三大辅助类 JUC 中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过 多时 Lock 锁的频繁操作.这三种辅助类为: CountDownLatch: 减少计数 ...
- Java并发编程之CyclicBarrier详解
简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...
最新文章
- c#结合emgucv进行人脸检测_人脸识别与测温结合在办公场景有哪些应用?
- boost::histogram::algorithm::project用法的测试程序
- 【学习笔记】JavaScript基础(一)
- python两个编程环境_多个python Behave环境安装文件
- 解决Eclipse java build path中Web App Libraries无法自动找到WEB-INF的lib目录
- 笔记本电脑下载matlab没有图标,win7系统安装matlab后找不到图标打不开如何解决...
- 【报告分享】2020-2021虎嗅洞察报告:聚焦私域流量,洞察增长变局.pdf(附下载链接)...
- Java —— Reflect反射机制
- 阿里面试官居然问我如何设计一个本地缓存
- 博弈论(Game Theory)入门学习笔记(持续更新)
- 员工管理能力怎么提高?不妨使用现代工时表软件
- 作为一个iOS攻城狮不得不了解的网络知识
- pythonos文件目录方法_PythonOS 文件目录方法
- vue轻量级后台管理系统基础模板
- 下厨房app竞品分析(产品和用户)
- 一张表了解四大物联网通信技术差异:NB-IoT 、LTEeMTC、LoRa与SigFox
- 斑马打印机Zebra 105SL每次开机就出纸
- python程序设计搜题软件_智慧职教云课堂APPPython程序设计(常州工业职业技术学院)期末考试搜题公众号答案...
- 节日EDM系列:圣诞节如何进行EDM数据营销
- AdmExpress 国际仓储转运系统/海淘转运系统 1.0 正式版发布