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 &param)
{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 &paramc)
{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 &paramc){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 &paramc){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 &paramc) {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创建和退出相关推荐

  1. Java并发编程之Thread类详解

    Thread类详解: 线程(Thread): 是操作系统进行调度的最小单位,Java中的线程是对操作系统线程的封装.本文从线程的创建到停止结合代码和具体实例分析一下关于java线程的一些常见问题. 线 ...

  2. c++并发编程之thread::join()和thread::detach()(转)

    thread::join(): 阻塞当前线程,直至 *this 所标识的线程完成其执行.*this 所标识的线程的完成同步于从 join() 的成功返回. 该方法简单暴力,主线程等待子进程期间什么都不 ...

  3. java并发编程之Thread.sleep方法详解

    Thread.sleep方法的作用: 使当前线程暂停执行一段时间,交出cpu的执行时间片,并且在暂停期间不会参与cpu时间片的获取.直到等待时间结束恢复到就绪状态,是否执行还要看OS的调度,或者在这段 ...

  4. java并发编程之thread.join()方法详解

    thread.join()方法的作用:保证线程的执行结果的可见性.原理是通过阻塞主线程实现的. 代码Demo如下: public class ThreadJoinDemo {public static ...

  5. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

  6. Python并发编程之threading模块

    Python并发编程之threading模块 threading 模块 1. Timer对象 2. Lock对象 3. RLock 4. 信号量和有边界的信号量 5. 事件 6. 条件变量 7. 使用 ...

  7. 并发编程之 Executor 线程池原理与源码解读

    并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...

  8. JUC 高并发编程之JUC三大辅助类

    JUC 高并发编程之JUC三大辅助类 JUC 中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过 多时 Lock 锁的频繁操作.这三种辅助类为: CountDownLatch: 减少计数 ...

  9. Java并发编程之CyclicBarrier详解

    简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...

最新文章

  1. c#结合emgucv进行人脸检测_人脸识别与测温结合在办公场景有哪些应用?
  2. boost::histogram::algorithm::project用法的测试程序
  3. 【学习笔记】JavaScript基础(一)
  4. python两个编程环境_多个python Behave环境安装文件
  5. 解决Eclipse java build path中Web App Libraries无法自动找到WEB-INF的lib目录
  6. 笔记本电脑下载matlab没有图标,win7系统安装matlab后找不到图标打不开如何解决...
  7. 【报告分享】2020-2021虎嗅洞察报告:聚焦私域流量,洞察增长变局.pdf(附下载链接)...
  8. Java —— Reflect反射机制
  9. 阿里面试官居然问我如何设计一个本地缓存
  10. 博弈论(Game Theory)入门学习笔记(持续更新)
  11. 员工管理能力怎么提高?不妨使用现代工时表软件
  12. 作为一个iOS攻城狮不得不了解的网络知识
  13. pythonos文件目录方法_PythonOS 文件目录方法
  14. vue轻量级后台管理系统基础模板
  15. 下厨房app竞品分析(产品和用户)
  16. 一张表了解四大物联网通信技术差异:NB-IoT 、LTEeMTC、LoRa与SigFox
  17. 斑马打印机Zebra 105SL每次开机就出纸
  18. python程序设计搜题软件_智慧职教云课堂APPPython程序设计(常州工业职业技术学院)期末考试搜题公众号答案...
  19. 节日EDM系列:圣诞节如何进行EDM数据营销
  20. AdmExpress 国际仓储转运系统/海淘转运系统 1.0 正式版发布

热门文章

  1. php使用phpmailer,php 发邮件(使用phpmailer类)
  2. 生成函数 计算机,吉布斯自由能热力学函数数值一键生成计算器
  3. 网卡信息检测与网络流量检测
  4. 网络安全的十大最佳网络扫描工具
  5. 横穿马路被电视台抓住采访时要沉着应答
  6. jmeter断言操作——响应断言
  7. WebGIS技术基础之HTML
  8. PDF技术(二)-Java实现Txt转PDF文件
  9. 简谈caffe中的LRN层
  10. C++线性代数库:Armadillo(犰狳) 使用初体验(二)