【Linux-11】多线程概念

太多的离别总是发生在六月,
太多的情绪,总是难以在这个带着伤感的季节里诉说~

目录:

1.线程概念

2.Linux线程VS进程

3.线程的实现

  3.1线程控制

  • 线程创建
  • 线程终止
  • 线程等待
  • 线程分离
  • 线程安全

4.线程间互斥的实现

5.线程间同步的实现


>>线程概念

线程是什么?

1.线程是一个执行流(运行代码,处理数据);

2.先看进程:传统操作系统中使用pcb来描述一个程序的运行--pcb就是进程;
3.Linux下通过pcb来模拟实现线程,pcb是一个轻量级进程;(这个轻量级进程因为共用大部分进程资源,相较于传统进程更加轻量化)同一个进程组中的pcb共用同一个虚拟地址空间,共享进程中大部分资源;

线程之间的独有与共享:

  独有:栈,寄存器,信号屏蔽字,errno,线程ID,

  共享:虚拟地址空间(数据段/代码段),文件描述符,信号处理方式,当前工作路径,用户id/组id,

>>线程VS进程

进程是资源分配的基本单位---因为程序运行时资源是分配给整个线程组(进程)的。

线程是CPU调度的基本单位---因为Linuxpcb是线程。

多任务的执行:既可以使用多线程也可以使用多进程,哪一个更好些呢?(分析优缺点/视场景而定)

多线程任务处理的优缺点分析:

多线程共用进程大部分资源;

1.线程间通信处理进程间的方式之外还有更简单的就是全局数据/传参-->线程间通信更加方便

2.共享虚拟地址空间:创建、销毁成本更低

3.线程间的调度相较于进程要更低

线程之间缺乏访问控制,有些系统调用、异常都会对整个进程造成影响;稳定性相较于进程更低。

场景:shell这种对主程序稳定安全性要求更高的程序就需要使用多线程,让子程序来处理任务。

>>线程控制

线程控制的接口都是库函数实现的:创建一个用户态线程让用户控制【使用库函数实现创建的线程称之为用户态线程,这个用户态线程在内核中使用一个轻量级进程实现调度】,但是程序的调度处理都只通过轻量级进程pcb实现。

Linux下的线程:用户态线程+轻量级进程

>>线程创建

int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg);
参数:
 thread:返回线程ID
 attr:设置线程的属性,attr为NULL表示使用默认属性
 start_routine:是个函数地址,线程启动后要执行的函数
 arg:传给线程启动函数的参数
 返回值:0 成功   !0 创建失败(errno)
线程id:       
 tid-线程地址空间首地址-方便用户操作线程       
 pcb->pid    轻量级进程id--LWP       
 pcb->tgid    线程组(进程)id,默认等于首线程的id

 

 

 

 

 

>>线程终止 

return    不能在main函数中return(退出的进程--导致所有线程退出),

void pthread_exit(void *retval)    退出线程自身,谁调谁退出,

retval:线程的退出返回值

int phread_cancel(pthread_t thread)  取消其他线程,让其它线程退出

thread:要取消的线程id

线程退出后,默认不会自动释放资源,保存自己的退出结果在线程独有的地址空间中,因此会造成资源泄漏。主线程退出,其他线程还可以正常运行。

>>线程等待

等待指定线程退出,获取这个线程的退出返回值,并且回收这个线程的资源;

一个线程有一个默认属性:joinable;处于joinable属性的线程退出后为了保存返回值,因此不会自动释放资源;如果不进行等待则会造成资源泄露。一个线程处于joinable状态的时候必须被等待!

int pthread_join(pthread_t thread, void **value_ptr);
参数
 thread:线程ID
 value_ptr:它指向一个指针,后者指向线程的返回值
返回值:0 成功;失败返回错误码

int pthread_join(pthread_t thread,void **retval);功能:阻塞等待指定线程退出,通过retval获取返回值。

void test(int **a){*a=(void*)1;} test(*b=10)

>>线程分离

 线程分离就是将线程joinable属性修改为detach属性。可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。

pthread_detach(pthread_self());

 joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *thread_run( void * arg ){
   pthread_detach(pthread_self());
   printf("%s\n", (char*)arg);
   return NULL;
}
int main( void ){
   pthread_t tid;
   if ( pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0 ) {
       printf("create thread error\n");
       return 1;
   }
   int ret = 0;
//要让线程先分离,再等待
   sleep(1);   
   if ( pthread_join(tid, NULL ) == 0 ) {
       printf("pthread wait success\n");
       ret = 0;
   } 
   else 
   {
       printf("pthread wait failed\n");
       ret = 1;
   }
   return ret;
}

线程若处于detach属性,则线程退休后将自动回收资源;并且这个线程不需要被等待,等待是毫无意义的,因为线程退出返回值占用的空间已经被收回了。

pthread_detach(pthread_t tid),

线程分离的使用场景:对线程的返回值不关心,

线程分离可以在任意线程中实现。

>>线程安全

常见不可重入的情况:

  1. 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的;
  2. 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构;
  3. 不可重入函数内使用了静态的数据结构。

常见可重入的情况:

  1. 不使用全局变量或静态变量;
  2. 不使用malloc或者new开辟出的空间;
  3. 不调用不可重入函数;
  4. 不返回静态或全局数据,所有数据都有函数的调用者提供;
  5. 使用本地数据,或者通过全局数据的本地拷贝来保护全局数据。

多个线程同时对临界资源进行访问而不会造成数据二义。

如何实现线程安全:同步+互斥

同步:对临界资源访问的时序合理性,

互斥:对临界资源同一时间访问的唯一性。

  • 可重入函数是线程安全函数的一种。
  • 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
  • 线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
  • 如果将对面临资源的访问加上锁,则这个函数是线程安全的,但如果这个可重入函数加锁未释放则会产生死锁,因此是不可重入的。

>>线程间互斥的实现

互斥锁

1.定义互斥锁变量 pthread_mutex_t

2.对互斥锁变量进行初始化 pthread_mutex_init(&mutex,&attr)

3对临界资源操作之前先加锁 pthread_mutex_lock(&mutex)

若可以加锁则直接修改计数,函数返回;负责挂起等待
    pthread_mutex_trylock /pthread_mutex_timedlock

4.对临界资源操作完毕后进行解锁
    pthread_mutex_unlock(&mutex);

5.销毁互斥锁
    pthread_mutex_destroy(&mutex);

互斥量加锁和解锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号

死锁:多个线程对资源进行竞争访问,但是因为推进顺序不当,导致相互等待,使程序无法往下进行

死锁产生的四个必要条件:

1.互斥条件            一个锁只有一个线程可以获取

2.不可剥夺条件        我加的锁别人不能解

3.请求与保持条件    拿着A锁,去请求B锁,但是获取不到B锁,也不释放A锁

4.环路等待条件        我拿着A锁请求B锁,对方拿着B锁请求A锁

死锁预防:破坏四个必要条件 【银行家算法,下一章详解

>>线程间同步的实现

等待+唤醒:操作条件不满足则等待,别人促使条件满足后唤醒等待

条件变量:条件变量实现同步:线程在对临界资源访问之前,先判断是否能够操作;若可以操作则线程直接操作;否则若不能操作;则条件变量提供等待功能;让pcb等待在队列上;其他线程促使操作条件满足,然后唤醒条件等待队列上的线程【eg.去餐馆吃面的例子以便理解】

 

 

 

 

 

 

 

~~bye~~

继续下一节总结!

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值