linux--线程(2续)
目录
- 线程控制
- 线程终止
- 线程等待
- 线程分离
- 线程互斥
- 进程线程间的互斥相关背景概念
- 互斥量mutex
- 互斥量实现原理探究
线程控制
线程终止
- 如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 线程可以调用pthread_ exit终止自己。
- 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
- return退出
#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(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<<endl; 27 return 0; 28 }
- 主线程运行10秒,新线程运行1秒,10秒后主线程return退出,新线程跟着也推出了。
- 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<<endl;28 return 0;
- 新线程运行5秒,主线程运行1秒当第五秒后进程通过exit退出。
3.pthread_exit函数
功能:线程终止 原型 void pthread_exit(void *value_ptr); 参数
value_ptr:value_ptr不要指向一个局部变量。 返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
#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 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 }
- 在主线程中调用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得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
- 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED。
- 如果thread线程是自己调用pthread_exit终止的-value_ptr所指向的单元存放的是传给pthread_exit的参 数。
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
线程分离
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());
#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 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"<<tid<<endl; 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"<<tid<<endl;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"<<tid<<endl;33 void*ret=NULL;34 pthread_join(tid,&ret);35 cout<<"pthtead quit codr:"<<(long long)ret<<endl;36 return 0;37 }
~
~
- 这里a(被所有线程访问)属于临界资源,访问临界资源的叫做临界区。
- 三个线程共享a,当10秒钟后主线程改掉为100后两个新线程也a的值也受到影响。
互斥量mutex
- 大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个 线程,其他线程无法获得这种变量。
- 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之 间的交互。
- 多个线程并发的操作共享变量,会带来一些问题。
之前的售票系统会出现票量为负数的情况,为什么呢?
if 语句判断条件为真以后,代码可以并发的切换到其他线程
usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段
–ticket 操作本身就不是一个原子操作
要解决以上问题,需要做到三点:代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临
界区。如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。
- 互斥量的接口
初始化互斥量
初始化互斥量有两种方法:
- 互斥量的接口
方法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的伪代码改一下
互斥锁底层简单介绍:
线程什么时间都可能换出,这里只介绍了其中一种。
以线程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续)相关推荐
- [转载]Linux 线程实现机制分析
自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads ...
- Linux 线程的创建与同步
Linux 线程的创建与同步 1.线程的定义 2.线程的创建和使用 3.理解线程的并发运行 3.线程同步 3.线程的实现 1.线程的定义 线程:进程内部的一条执行路径.是资源调度和执行的基本单位. 进 ...
- linux 线程操作问题undefined reference to ‘pthread_create‘的解决办法(cmake)
linux 线程操作问题undefined reference to 'pthread_create'的解决办法(cmake) 参考文章: (1)linux 线程操作问题undefined refer ...
- linux线程的实现【转】
转自:http://www.cnblogs.com/zhaoyl/p/3620204.html 首先从OS设计原理上阐明三种线程:内核线程.轻量级进程.用户线程 内核线程 内核线程就是内核的分身,一个 ...
- linux 线程 进程经典文章
进程是程 序在计算机上的一次执行活动.当你运行一个程序,你就启动了一个进程.显然,程序是 死的(静态的),进程是活的(动态的).进程可以分为系统进程和用户进程.凡是用于完成操作系统的各种功能的进程就是 ...
- linux 线程--内核线程、用户线程实现方法
Linux上进程分3种,内核线程(或者叫核心进程).用户进程.用户线程 内核线程拥有 进程描述符.PID.进程正文段.核心堆栈 当和用户进程拥有相同的static_prio 时,内核线程有机会得到更多 ...
- Linux 线程与进程,以及通信
http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...
- Linux线程-互斥锁pthread_mutex_t
Linux线程-互斥锁pthread_mutex_t 在线程实际运行过程中,我们经常需要多个线程保持同步.这时可以用互斥锁来完成任务:互斥锁的使用过程中,主要有pthread_mutex_init, ...
- 【Linux开发】彻底释放Linux线程的资源
Linux系统中程序的线程资源是有限的,表现为对于一个程序其能同时运行的线程数是有限的.而默认的条件下,一个线程结束后,其对应的资源不会被释放,于是,如果在一个程序中,反复建立线程,而线程又默认的退出 ...
- c++ linux 线程等待与唤醒_Linux线程同步(互斥量、信号量、条件变量、生产消费者模型)...
为什么要线程同步? 线程间有很多共享资源,都对一个共享数据读写操作,线程操作共享资源的先后顺序不确定,可能会造成数据的冲突 看一个例子 两个线程屏行对全局变量count++ (采用一个val值作为中间 ...
最新文章
- linux动态链接库的使用,Linux动态库soname的使用
- 作者:郭旦怀(1973-),男,博士,中国科学院计算机网络信息中心副研究员、硕士生导师。...
- android创建wifi热点,Android 4.0.3创建wifi热点API
- idea 包存在提示不存在
- Concurrency in C# Cookbook中文翻译 :c#的并发烹饪书
- (最详细)红米手机5 Plus的USB调试模式在哪里开启的方法
- 在线词云工具生成词云图——Wordart的使用
- KeyError: 'labels [189] not contained in axis' Python DataFrame 合并后使用loc进行索引的时候出错问题分析以及解决方案
- 如何改善物流行业项目管理?
- C语言源程序作业完成系统,C语言源程序的自动评判系统
- python股票全套系统_用python来炒股三 炒股交易系统(法则)
- 【visio】六步完成跨职能部门业务流程图
- SAP新的Activate实施方法论都有什么变化
- linux创建任务栏图标
- 测试面试之Linux
- Vue——axios的二次封装
- 树莓派挂载硬盘以及播放视频
- c3p0详细配置(c3p0-config.xml)及使用
- iOS学习:一个iOS开发者的修真之路
- 迁移系统激活已有的正版windows11专业版及office家庭学生版步骤