linux--线程(2续)

目录

  • 线程控制
    • 线程终止
    • 线程等待
    • 线程分离
  • 线程互斥
    • 进程线程间的互斥相关背景概念
    • 互斥量mutex
    • 互斥量实现原理探究

线程控制

线程终止

  • 如果需要只终止某个线程而不终止整个进程,可以有三种方法:
  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
  1. return退出
#include
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<pthread.h>
  7 using namespace std;
  8 void* run(void*arg){
     
  9   while(1){
     
 10     cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;                        
 11  sleep(1);                                             
 12                                      
 13   }               
 14   return (void*)10;                             
 15 }                                    
 16 int main(){
                   
 17   pthread_t tid;                        
 18   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 19   while(1){
     
 20     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;         
 21     sleep(10);                                                   
 22  break;                                                   
 23   }                                   
 24  // void*ret=NULL;                                                                                                                          
 25  // pthread_join(tid,&ret);                   
 26  // cout<<"pthtead quit codr:"<<(long long)ret<
 27   return 0;           
 28 }


linux--线程(2续)_第1张图片

  • 主线程运行10秒,新线程运行1秒,10秒后主线程return退出,新线程跟着也推出了。
  1. exit退出
 1 #include<iostream>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>                                                                                                                          
  6 #include<unistd.h>
  7 #include<pthread.h>
  8 using namespace std;
  9 void* run(void*arg){
     
 10   while(1){
                                                                              
 11     cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 12  sleep(5);                           
 13  exit(-1);        
 14   }                                             
 15   return (void*)10;                  
 16 }                        
 17 int main(){
                                  
 18   pthread_t tid;       
 19   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 20   while(1){
                                                             
 21     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;       
 22     sleep(1);                                             
 23                                       
 24   }                                                     
 25  // void*ret=NULL;                            
 26  // pthread_join(tid,&ret);                                                                                       
 27  // cout<<"pthtead quit codr:"<<(long long)ret<
 28   return 0;

linux--线程(2续)_第2张图片

  • 新线程运行5秒,主线程运行1秒当第五秒后进程通过exit退出。

3.pthread_exit函数

功能:线程终止 原型 void pthread_exit(void *value_ptr); 参数
value_ptr:value_ptr不要指向一个局部变量。 返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

#include
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>
  6 #include<unistd.h>
  7 #include<pthread.h>
  8 using namespace std;
  9 void* run(void*arg){
     
 10   //while(1){                                                                       
 11     cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 12  sleep(5);                           
 13  pthread_exit((void*)10);
 14  // }                                           
 15  // return (void*)10;                
 16 }                        
 17 int main(){
                                  
 18   pthread_t tid;       
 19   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 20 //  while(1){                                                                                                                               
 21     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;       
 22     sleep(1);                                             
 23                                       
 24 //  }                                                   
 25   void*ret=NULL;                              
 26   pthread_join(tid,&ret);                                                                                         
 27   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 28   return 0;

在这里插入图片描述

  • 新线程调用pthread_exit()终止自己,同时返回自己的退出码。
    4.pthread_cancel函数

功能:取消一个执行中的线程 原型 int pthread_cancel(pthread_t thread); 参数 thread:线程ID
返回值:成功返回0;失败返回错误码
两种取消方式:
1.别人取消 2,自己取消自己

1,别人取消

  1 #include<iostream>                                                                                                                                                                                                                    
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>
  6 #include<unistd.h>
  7 #include<pthread.h>
  8 using namespace std;
  9 void* run(void*arg){
     
 10   while(1){
     
 11       cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 12       sleep(2);
 13 // pthread_exit((void*)10);
 14   }
 15  return (void*)10;
 16 }
 17 int main(){
     
 18   pthread_t tid;
 19   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 20 //  while(1){
     
 21     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
 22     sleep(10);
 23 
 24 //  }
 25 pthread_cancel(tid);
 26 cout<<"new pthread"<<tid<<endl;
 27   void*ret=NULL;
 28   pthread_join(tid,&ret);
 29   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 30   return 0;
 31 }

linux--线程(2续)_第3张图片

  • 在主线程中调用pthread_cancal()杀死新进程,同时获取它的退出码 -1。

线程等待

  • 线程为什么要等待?
  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

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

  1 #include<iostream>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<pthread.h>
  7 using namespace std;
  8 void* run(void*arg){
     
  9   while(1){
     
 10     cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 11  sleep(5);
 12  break;                                                                                                                                     
 13   }
 14   return (void*)10;
 15 }
 16 int main(){
     
 17   pthread_t tid;
 18   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 19 //  while(1){
     
 20     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
 21 // break;
 22 //  }
 23   void*ret=NULL;
 24   pthread_join(tid,&ret);
 25   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 26   return 0;
 27 }

在这里插入图片描述

  • 当主线程运行完时,新线程还没有执行完主线程就会等待新线程,当新线程执行完后会返回他的退出码,主线程会捕获新线程的退出码。
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<pthread.h>
  7 using namespace std;
  8 void* run(void*arg){
     
  9   while(1){
     
 10     cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;                        
 11  sleep(5);                                             
 12  int a=0;                            
 13  a=5/a;           
 14 
 15  break;
 16   }
 17   return (void*)10;
 18 }
 19 int main(){
     
 20   pthread_t tid;
 21   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 22 //  while(1){
     
 23     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
 24 // break;
 25 //  }
 26   void*ret=NULL;
 27   pthread_join(tid,&ret);
 28   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 29   return 0;                                                                                                                                 
 30 }

在这里插入图片描述

  • 当新线程异常时,整个进程就会出现异常。同时主线程也获取不到新线程的退出码。

    • 调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的-value_ptr所指向的单元存放的是传给pthread_exit的参 数。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。

线程分离

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

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:

pthread_detach(pthread_self());
#include
  8 using namespace std;
  9 void* run(void*arg){
     
 10   pthread_detach(pthread_self());
 11   while(1){
     
 12       cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 13       sleep(1);
 14       break;
 15 
 16 // pthread_exit((void*)10);
 17   }
 18  return (void*)10;
 19 }
 20 int main(){
     
 21   pthread_t tid;
 22   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 23  // while(1){
     
 24     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
 25     sleep(2);
 26 
 27 //  }
 28 //pthread_cancel(tid);
 29 //cout<<"new pthread"<
 30   void*ret=NULL;
 31   pthread_join(tid,&ret);
 32   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 33   return 0;
 34 }

在这里插入图片描述

  • 主线程没有拿到线程的退出码。线程退出时,资源自动释放
  1 #include<iostream>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>
  6 #include<unistd.h>
  7 #include<pthread.h>
  8 using namespace std;
  9 void* run(void*arg){
     
 10   pthread_detach(pthread_self());
 11   while(1){
     
 12       cout<<(char*)arg<<pthread_self()<<"pid"<<getpid()<<endl;
 13       sleep(1);
 14       int a=0;
 15       a=5/a;                                                                                                                                                                                                                          
 16       break;
 17 
 18 // pthread_exit((void*)10);
 19   }
 20  return (void*)10;
 21 }
 22 int main(){
     
 23   pthread_t tid;
 24   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 25  // while(1){
     
 26     cout<<"main:"<<pthread_self()<<"pid:"<<getpid()<<endl;
 27     sleep(2);
 28 
 29 //  }
 30 //pthread_cancel(tid);
 31 //cout<<"new pthread"<
 32   void*ret=NULL;
 33   pthread_join(tid,&ret);
 34   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 35   return 0;
 36 }
~
~

在这里插入图片描述

  • 虽然线程分离 了但是只要线程出现异常,进程就会异常退出

线程互斥

进程线程间的互斥相关背景概念

临界资源:多线程执行流共享的资源就叫做临界资源 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完 成

  1 #include<iostream>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>
  6 #include<unistd.h>
  7 #include<pthread.h>
  8 using namespace std;
  9 int a=10;
 10 void* run(void*arg){
     
 11   while(1){
     
 12     cout<<(char*)arg<<","<<pthread_self()<<",,,pid"<<getpid()<<endl;
 13       cout<<(char*)arg<<",a:"<<a<<",address"<<&a<<endl;                                                                                                                                                                               
 14       sleep(1);
 15   }
 16  return (void*)10;
 17 }
 18 int main(){
     
 19   pthread_t tid;
 20   pthread_t pidd;
 21   pthread_create(&tid,NULL,run,(void*)"pthread 1");
 22   pthread_create(&pidd,NULL,run,(void*)"pthread 2");
 23   sleep(10);
 24   a=100;
 25  // while(1){
     
 26     cout<<"main:"<<pthread_self()<<",pid:"<<getpid()<<endl;
 27     cout<<"main:a:"<<a<<",address:"<<&a<<endl;
 28     sleep(2);
 29 
 30 //  }
 31 //pthread_cancel(tid);
 32 //cout<<"new pthread"<
 33   void*ret=NULL;
 34   pthread_join(tid,&ret);
 35   cout<<"pthtead quit codr:"<<(long long)ret<<endl;
 36   return 0;
 37 }
~
~

linux--线程(2续)_第4张图片

  • 这里a(被所有线程访问)属于临界资源,访问临界资源的叫做临界区。
  • 三个线程共享a,当10秒钟后主线程改掉为100后两个新线程也a的值也受到影响。

互斥量mutex

  • 大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个 线程,其他线程无法获得这种变量。
  • 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之 间的交互。
  • 多个线程并发的操作共享变量,会带来一些问题。

之前的售票系统会出现票量为负数的情况,为什么呢?

  • if 语句判断条件为真以后,代码可以并发的切换到其他线程

  • usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段

  • –ticket 操作本身就不是一个原子操作
    要解决以上问题,需要做到三点:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。

  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临
    界区。

  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
    要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。
    linux--线程(2续)_第5张图片

    • 互斥量的接口
      初始化互斥量
      初始化互斥量有两种方法:
  • 方法1,静态分配:

pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER

  • 方法2,动态分配:

>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);

参数: mutex:要初始化的互斥量 attr:NULL

销毁互斥量
销毁互斥量需要注意:

使用PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
不要销毁一个已经加锁的互斥量
已经销毁的互斥量,要确保后面不会有线程再尝试加锁

int pthread_mutex_destroy(pthread_mutex_t *mutex)

互斥量加锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回错误号

调用pthread_ lock 时,可能会遇到以下情况:

互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量, 那么pthread_
lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁。

改进的售票系统

 1: tick.cpp  ⮀                                                                                                                                                                                                                                                                                                                                                                                                                                                         ⮂⮂ buffers 
    1 #include<iostream>
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 #include<pthread.h>
    5 using namespace std;
    6 int ticket=10000;
    7 pthread_mutex_t lock;
    8 void *run(void *aig){
     
    9   //int num=(int)aig;
   10   usleep(1000);
   11   while(1){
     
   12     pthread_mutex_lock(&lock);
   13   //  usleep(1000);                                                                                                                                                                                                                                                                                                                                                                                                                                                            
   14     if(ticket>0){
     
   15      usleep(1000);
   16       cout<<"pthread "<<(char*)aig<<",ticket:"<<ticket<<endl;
   17       ticket--;
   18       pthread_mutex_unlock(&lock);
   19 
   20     }else{
     
   21       pthread_mutex_unlock(&lock);
   22       break;
   23     }
   24   }
   25 
   26 
W> 27 }
   28 int main(){
     
   29   pthread_t tid[4];
W> 30   char*tic[4]={
     "1","2","3","4"};
   31  // pthread_t ti,d1;
   32  //.autorelabel pthread_t tid2;
   33  pthread_mutex_init(&lock,NULL);
   34   int i=0;
   35   for(;i<4;i++){
     
   36   pthread_create(tid+i,NULL,run,(void*)tic[i]);
   37 
   38   }
   39   for(i=0;i<4;i++){
     
   40     pthread_join(tid[i],NULL);
   41   }
   42   pthread_mutex_destroy(&lock);
   43 }
  ~
  ~
  ~
  ~

互斥量实现原理探究

  • 经过上面的例子,大家已经意识到单纯的i++ 或者++i 都不是原子的,有可能会有数据一致性问题
  • 为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 现在我们把lock和unlock的伪代码改一下

linux--线程(2续)_第6张图片
互斥锁底层简单介绍:
线程什么时间都可能换出,这里只介绍了其中一种。

以线程1为主线程介绍,当线程1进入时执行movb后把0放到%al寄存器中,接着执行xchgb语句(而mutex内存区默认从1开始),把%al和mutex的值换了,当执行完后,线程1杯切走,要进行上下文保护,把%al寄存器中的值放到特定寄存器中,而mutex的值不变还为0。接着当线程2进入时执行movb后把0放到%al寄存器中,接着执行xchgb语句把%al和mutex的值换了,当执行if因为%alzhong为0不满足而执行else语句后进入挂起状态。接着线程1切回从刚才切出的地方重新执行出现把特定寄存器的值放入%al寄存器中(恢复现场),在执行if后条件满足锁申请成功

linux--线程(2续)_第7张图片

你可能感兴趣的:(linux)