Linux多线程开发
线程是操作系统执行调度的最小单位,本身不拥有资源
进程线程区别可在Linux多进程开发查看
https://blog.csdn.net/weixin_45670785/article/details/125352684
线程的共享和非共享资源
-
共享资源(内核区)
进程ID和父进程ID,进程组ID和会话ID,用户ID和用户组ID,文件描述符表,信号处置
文件系统相关信息:文件权限掩码(umask),当前工作目录
虚拟地址空间(除栈,.txt文件)
-
非共享资源
线程ID,信号掩码,线程持有数据,error变量(errno),实时调度策略和优先级
栈,本地变量和函数的调用链接信息
Linux相关指令
1.查看指定进程的LWP号
ps -Lf pid
2.获取当前的线程的线程ID
pthread_t pthread_self(void);
3.创建子线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数:
thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中。
attr : 设置线程的属性,一般使用默认值,NULL
start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码
arg : 给第三个参数使用,传参
返回值:
成功:0
失败:返回错误号。这个错误号和之前errno不太一样。
获取错误号的信息: char * strerror(int errnum);
4.线程分离
int pthread_detach(pthread_t thread);
功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统。
1.不能多次分离,会产生不可预料的行为。
2.不能去连接一个已经分离的线程,会报错。
3.不能和阻塞join函数一起使用,与destroy()类似
5.回收子线程资源,阻塞
int pthread_join(pthread_t thread, void retval);//线程id,返回值
阻塞函数,调用一次回收一次子线程资源,一般在主线程使用
6.线程属性函数
int pthread_attr_init(pthread_attr_t *attr);
初始化线程属性变量
int pthread_attr_destroy(pthread_attr_t *attr);
释放线程属性的资源
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
获取线程分离的状态属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
设置线程分离的状态属性
线程同步
线程的优势在于能够通过全局变量来共享信息,但是要确保临界区(原子地访问某一共享资源的代码片段)不被修改,也就是某一线程不会读取正在由其他线程修改的变量,或者多个线程同时修改同一变量。并且写的权限高于读的权限。
线程同步:线程同步是一种机制,用于协调多个线程之间的执行顺序和访问共享资源的方式,以确保线程之间的正确交互和数据一致性。通过线程同步,可以避免多个线程同时访问共享资源导致的竞态条件和数据不一致问题。常见的线程同步方式包括互斥锁、信号量、条件变量等。
互斥锁(互斥量)
定义
优势
1.提供了以排他方式防止数据结构被并发修改的方法
2.保证一个线程对任意共享资源的原子访问。
3.解决线程同步和数据安全问题
读写锁
读写锁:允许多个线程同时读共享数据,而对写操作是互斥的
读写锁的特点:
如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
如果有其它线程写数据,则其它线程都不允许读、写操作。
写是独占的,写的优先级高。
读锁不用阻塞,效率更高
(ps: 相关API调用应该没人想看吧,逃~)
条件变量
条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。
对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量进制:包括匿名线程信号量和命名线程信号量
死锁
定义:两个或两个以上的线程在执行过程中,因抢夺共享资源而造成一种相互等待的现象,若无外力作用下,无法继续推进
场景
(1)忘记释放锁
(2)重复加锁
(3)多线程多锁,抢占锁资源
必要条件
- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
死锁预防:破坏上述四个条件
在程序运行之前预防发生死锁。
-
破坏互斥条件
例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。 -
破坏占有和等待条件
一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 -
破坏不可抢占条件
-
破坏环路等待
给资源统一编号,进程只能按编号顺序来请求资源。
死锁避免则是安全状态和银行家算法
多线程模型
- 线程是运行在进程中的一个“逻辑流”,单进程中可以运行多个线程,线程可以访问隶属进程的资源,比如文件描述符列表、进程空间、代码、全局数据、堆、共享库等,优势可从可从系统开销,调度考虑,联系进程线程区别。
- 当服务器与客户端 TCP 完成连接后,通过 pthread_create() 函数创建线程,然后将「已连接 Socket」的文件描述符传递给线程函数,接着在线程里和客户端进行通信,从而达到并发处理的目的。
- 注意事项
◼父进程accept后会把socket放入一个队列。然后线程从这个队列中取socket操作
◼这个队列是全局的,每个线程都会操作,为了避免多线程竞争,线程在操作这个队列前要加锁。