多线程(创建,终止,等待,分离),线程安全

  概念:线程是进程中的一个执行流程

线程是cpu进行调度的基本单元(调度一段代码的执行是通过线程来完成的);进程是系统进行资源分配的基本单元。Linux下一个进程中是可以存在多个pcb的,一个pcb就是一个执行流程。

总之:线程是什么:cpu调度执行的基本基本单元(linux下通过pcb实现)                                                   线程与进程区别:从资源和调度上进行区别                                                                                       多任务处理中多进程与多线程的优缺点:健壮性,通信灵活性,创建与销毁的成本,切换调度成本

          线程如何独立运行(线程间的独立数据(栈。上下文数据,errno,信号屏蔽字)与共享数据(虚拟地址空间,文件信息)

在多任务处理中,启动多少多执行流比较合适?线程控制: 线程(创建,终止,等待,分离) 

 创建线程

功能:创建一个新的线程
原型
    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:传给线程启动函数start_routine的参数
返回值:成功返回0;失败返回错误码

例如:

 

线程的查看:ps-l 选项进行查看(查看的是轻量级的进程信息)

 pthread_create接口的第一个参数获取到的tid,并不是轻量级进程的lwp/pid

线程终止:
1. 从线程函数 return 。这种方法对主线程不适用 , main 函数 return 相当于调用 exit
2. 线程可以调用 pthread_ exit 终止自己。
3. 一个线程可以调用 pthread_ cancel 终止同一进程中的另一个线程。
功能:线程终止
原型
    void pthread_exit(void *value_ptr);
参数
    value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身
功能:取消一个执行中的线程
原型
    int pthread_cancel(pthread_t thread);参数
    thread:线程ID
返回值:成功返回0;失败返回错误码
线程等待
为什么需要线程等待?
 1、已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
 2、创建新的线程不会复用刚才退出线程的地址空间。

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

 分离线程

1、默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进行 pthread_join 操作,否则无法释放资源,从而造成系统泄漏。
2、如果不关心线程的返回值, join 是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

int pthread_detach(pthread_t thread);//设置指定线程分离属性为datach
线程安全:
多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操
作,并且没有锁保护的情况下,会出现该问题。

 接口:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER//互斥锁变量类型
 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t 
*restrict attr);//初始化
 参数:
 mutex:要初始化的互斥量,互斥锁变量的地址
 attr:NULL//互斥锁变量属性
//加锁
//在访问资源前加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);//阻塞加锁,加不上则一直等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);/非阻塞加锁,加不上则立即报错返回
调用 pthread_ lock 时,可能会遇到以下情况
1、互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功。
2、发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥 量,那么pthread_ lock 调用会陷入阻塞 ( 执行流被挂起 ) ,等待互斥量解锁。
//访问资源完毕解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);/非阻塞加锁,加不上则立即报错返回
返回值:成功返回0,失败返回错误号

//销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex)//销毁互斥锁
1、使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
2、不要销毁一个已经加锁的互斥量
3、已经销毁的互斥量,要确保后面不会有线程再尝试加锁

代码演示:

 

 

常见锁概念

死锁:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的 资源而处于的一种永久等待状态

 避免:1银行家算法2死锁检测算法

Linux 线程同步

条件变量和互斥锁搭配使用

接口:

初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict 
attr);
参数:
 cond:要初始化的条件变量
 attr:NULL
阻塞接口//条件变量和互斥锁搭配使用,体现在阻塞
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
 参数:
 cond:要在这个条件变量上等待
 mutex:互斥量
 唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒至少一个阻塞队列中的线程
int pthread_cond_signal(pthread_cond_t *cond);//唤醒所有阻塞队列中的线程
int pthread_cond_destroy(pthread_cond_t *cond)//销毁窗口

例子:

     #include<stdio.h>
    2 #include<pthread.h>
    3 int counter=0;//0表示没有饭,1表示有饭
    4 pthread_mutex_t mutex;
    5 pthread_cond_t cond;
W>  6 void *curstomer(void*arg)
    7 {
    8   while(1)
    9   {
   10     pthread_mutex_lock(&mutex);
   11     if(counter==0)//
   12     {
   13       pthread_cond_wait(&cond,&mutex);
   14     }
   15   printf("好吃,再来一碗\n");
   16   counter=0;//吃完了就没饭了
   17   pthread_cond_signal(&cond);//唤醒厨师
   18   pthread_mutex_unlock(&mutex);//解锁
   19   }
   20     return 0;
   21 }
W> 22   void *cook(void *arg)
   23 {
   24   while(1)
   25   {
   26     pthread_mutex_lock(&mutex);
   27     if(counter==1)
   28     {
   29       pthread_cond_wait(&cond,&mutex);                                                                                                                            
   30     }
   31   printf("饭好了,快来吃\n");
   32   counter=1;
   33   pthread_cond_signal(&cond);
   34   pthread_mutex_unlock(&mutex);
 }
   36   return 0;
   37 }
   38 int main()
   39 {
   40 pthread_t cus_tid,cook_tid;
   41 int ret;
   42 pthread_mutex_init(&mutex,NULL);
   43 pthread_cond_init(&cond,NULL);
   44 pthread_cond_init(&cond,NULL);
   45  ret=pthread_create(&cus_tid,NULL,curstomer,NULL);
   46 if(ret!=0)
   47 {
   48   printf("pthread_creat error\n");
   49   return -1;
   50 }
   51  ret=pthread_create(&cook_tid,NULL,cook,NULL);
   52 if(ret!=0)
   53 {
   54   printf("pthread_create error\n");
   55   return -1;
   56 }
   57   pthread_join(cus_tid,NULL);
   58   pthread_join(cook_tid,NULL);
   59   pthread_mutex_destroy(&mutex);
   60   pthread_cond_destroy(&cond);
   62   return 0;
   63 }

 交替阻塞,交替唤醒的过程。若修改代码实现多对多,例如:

 pthread_t cus_tid[4],cook_tid[4];;

 会出现卡死的情况。原因在于:

 修改后的代码如下:

   13     {
   14       pthread_cond_wait(&cond_cus,&mutex);
   15     }
   16   printf("好吃,再来一碗\n");
   17   counter--;//吃完了就没饭了
   18   pthread_cond_signal(&cond_cook);//唤醒厨师
   19   pthread_mutex_unlock(&mutex);//解锁
   20   }
   21     return 0;
   22 }
   23   void *cook(void *arg)
   24 {
   25   while(1)
   26   {
   27     pthread_mutex_lock(&mutex);
   28     while(counter>0)
   29     {                                                                                                                                                             
   30       pthread_cond_wait(&cond_cook,&mutex);
   31     }
   32   printf("饭好了,快来吃\n");
   33   counter++;
   34   pthread_cond_signal(&cond_cus);
 pthread_mutex_unlock(&mutex);
   36   }
   37   return 0;
   38 }
   39 int main()
   40 {
   41 pthread_t cus_tid[4],cook_tid[4];;
   42 int ret;
   43 pthread_mutex_init(&mutex,NULL);
   44 pthread_cond_init(&cond_cus,NULL);
   45 pthread_cond_init(&cond_cook,NULL);
   46 for(int i=0;i<4;i++)
   47 {
   48 
   49 
   50  ret=pthread_create(&cus_tid[i],NULL,curstomer,NULL);
   51 if(ret!=0)
   52 {
   53   printf("pthread_creat error\n");
   54   return -1;
   55 }
   56  ret=pthread_create(&cook_tid[i],NULL,cook,NULL);
   57 if(ret!=0)
   58 {
   59   printf("pthread_create error\n");
   60   return -1;
   61 }
生产者消费者模型

 生产者消费者模型优点 :解耦、支持并发、支持忙闲不均

信号量:

#include <semaphore.h>//头文件
sem_t sem 信号量变量类型
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
 pshared:0表示线程间共享,非零表示进程间共享(进程间), 0线程间
 value:信号量初始值

p操作:
  功能:等待信号量,会将信号量的值减1
  int sem_wait(sem_t *sem);//阻塞接口
   int sem_trywait(sem_t *sem);//非阻塞接口
   int sesm_timedwait(sem_t *sem,struct timespec *soec);//阻塞但有限制时长

v操作:
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);

//销毁
int sem_destroy(sem_t *sem)

 

 线程池:

线程安全的单例模式:某些类 , 只应该具有一个对象 ( 实例 ), 就称之为单例
好处:
1、资源角度:资源在内存中只能有一份
2、数据角度:如果只有一个对象,则数据无论在什么时候获取都是一致的

 

 多线程:
线程概念:线程是什么?
进程与线程进行多任务处理的不同之处?

线程间的共享与独有
线程控制:等待与分离是什么?

线程安全:
概念:线程安全是什么?实现:同步与互斥(概念)
互斥的实现--互斥锁:互斥锁实现互斥的原理?
死锁:概念-死锁是什么? 如何产生的?如何预防?如何避免?

同步的实现--条件变量&信号量(各自的本质是什么)

生产者与消费者模型:应用场景,优点,实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值