Linux学习七(线程)
Linux学习七 : 线程
线程概述
线程是轻量级的进程
:在操作系统中,将一个进程划分为多个执行单元,每个单元拥有自己的堆栈、程序计数器和资源使用情况,但共享同一进程的地址空间和文件描述符等资源,这些执行单元就是线程。进程是资源分配的最小单位,线程是系统调度的最小单位。
线程特点:
- 线程的创建、切换和销毁都更加高效,占用的系统资源少。
- 线程提供了一种并发执行的机制,使得多个任务可以在同一个进程中并行执行。
- 不同线程之间可以通过共享内存等机制进行通信和同步,数据共享更加便捷,且避免了进程切换的开销。
- 线程能够利用多核处理器的并发执行有事,提高系统的吞吐能力,一定程度下可以提升系统性能。CPU有多少个核就可以开多少个线程。
- 由于线程共享进程的地址空间,因此需要注意线程之间的数据竞争和同步,调度资源并发编程问题,充分考虑线程安全和并发控制,以确保程序的正确性和稳定性。
线程的实现主要通过引入线程组的概念来实现。每个进程都有一个主线程,也就是创建该进程的线程,当创建新的线程时,新的线程将与主线程一起组成一个线程组。
多进程模型
:每个进程都有独立的地址空间、文件表信号表等资源。多线程模型
:线程组内线程共享地址内存空间,文件表信号表等资源。
实现多线程的目的
:为了更高效的利用CPU资源,线程相比于进程,创建、切换、通信、终止成本降低。线程(任务)的数量要根据具体的处理器数量来决定。假设只有一个处理器,那么划分太多线程可能会适得其反。因为很多时间都花在任务切换上了。
因此,在设计并发系统之前,一方面我们需要做好对于硬件性能的了解,另一方面需要对我们的任务有足够的认识。
线程的缺点
- 安全性差:线程相互耦合,一个线程出错会影响其他线程。
- 编程难度大:
- 调试困难
- 竞态条件和死锁:需要线程同步管理。
线程操作
创建线程
pthread_create:Linux下用于创建线程的函数,需要给线程一个处理函数,否则线程无法工作。
线程属性:可以在线程创建时指定的一组特性,用于控制线程的行为和特性。比如线程调度优先级,调度策略,线程栈空间大小。
pthread_self():获取线程id。
线程退出
:只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。
#include <pthread.h>
void pthread_exit(void *retval);retval是子线程的主线程会得到该数据。如果不需要使用,指定为NULL
线程回收
:等待一个线程结束并回收器返回值和线程资源。pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程。
#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);
回收子线程数据的方法:
- 用子线程栈区是不可以的,因为每个线程都有一个独立的栈区,子线程结束以后,数据不能回传给主线程。
- 用堆区和全局数据区,是可以的,位于同一虚拟地址空间中的线程,虽然不能共享栈区数据,但是可以共享全局数据区和堆区数据,因此在子线程退出的时候可以将传出数据存储到全局变量、静态变量或者堆内存中。
- 回传到主线程栈区是可以的,一般情况下主线程栈区都是最后退出的。
1 |
|
最终运行的结果中子线程栈空间的数据丢失。
线程分离
:将一个线程从它的创建线程中分离出来,使得该线程不会成为“僵尸线程”,从而避免资源泄露和内存占用的问题。不需要其他线程调用pthread_join()函数来等待其终止,但是在终止后不能再重连。
pthread_detach(tid);线程分离。
1 |
|
线程终止
:
- 自然终止
- 调用exit()函数:立即终止整个进程,包括所有线程。
- pthread_cancel()函数:线程取消
- pthread_kill()函数:杀死线程。
- pthread_exit()函数:显式退出线程
线程取消
:在一个线程中杀死另一个线程,需要分两步:
- 在线程A中调用线程取消函数pthread_cancel,指定杀死线程B,这时候线程B是死不了的。
- 在线程B中进程一次系统调用(从用户区切换到内核区),否则线程B可以一直运行。
线程取消的类型:
- 异步取消:立即取消线程,但是可能会出现无法清理,资源泄露的情况。
- 延迟取消:在取消点检查是否请求取消。如IO操作,线程等待函数等。
pthread_cancel()函数:线程取消。
pthread_setcanceltype()函数:设置线程取消的取消类型。
C++线程类
C++11中提供的线程类为std::thread,基于这个类创建一个新的线程非常的简单,只需要提供线程函数或者函数对象即可,并且可以同时指定线程函数的参数。作为一个类,类的构造函数,移动构造都有,但是不允许拷贝线程对象。
std::thread t1(); 可以传线程执行函数,以及函数所需参数。
std::thread::id get_id() const noexcept; 获取线程ID。
void join(); 回收线程
void detch(); 线程分离
bool joinable() const noexcept; 用于判断主线程和子线程是否处理关联(连接)状态。
// move (1)
thread& operator= (thread&& other) noexcept;
// copy [deleted] (2)
thread& operator= (const other&) = delete; 线程中的资源是不能被复制的,因此通过=操作符进行赋值操作最终并不会得到两个完全相同的对象。只能进行线程资源的转移,直接拷贝赋值是不被允许的。
static unsigned hardware_concurrency() noexcept; 获取当前计算机的CPU核数,根据这个结果在程序中创建出数量相等的线程,每个线程独自占有一个CPU核心,这些线程就不用分时复用CPU时间片,此时程序的并发效率是最高的。
1 |
|
参考列表
https://subingwen.cn/linux/thread/
https://www.bilibili.com/video/BV1Xb4y1u7gR/
https://www.bilibili.com/video/BV1d841117SH