目录
3、阻塞等待线程退出,获取线程退出状态--pthread_join
一、线程的概念:
主线程和子线程共享:
.text
.bss
.data
堆
动态加载区
环境变量
命令行参数
-通信:全局变量,堆
不共享
一共五个线程,栈区被平均分成五块
查看指定线程的LWP号:
线程号和线程
ID
是有区别的
线程号是给内核看的
查看方式
找到程序的进程
ID
ps -Lf pid
(1)线程的创建:
1.创建线程‐‐pthread_createint pthread_create( pthread_t *thread), //线程ID = 无符号长整型void *(*start_routine)(void *), //线程处理函数void *arg); //线程处理函数参数:pthread:传出参数,线程创建成功之后,会被设置一个合适的值start_routine:子线程的处理函数arg: 回调函数的参数返回值:成功:0主线程先退出,子线程会被强制结束验证线程直接共享全局变量
Demo:
vi pthread_create.c
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
void* child_handle(void *arg)
{
printf("child pthread id : %ld\n",pthread_self());
return 0;
}
int main()
{
pthread_t phid;
int ret;
ret=pthread_create(&phid,NULL,child_handle,NULL);
if( ret ! = 0)
{
printf("error number is %d\n",ret);
printf("%s \n",strerror(ret)); //strerror函数返回错误信息是一个字符串
}
printf("parent pthread id : %ld\n",pthread_self());
for(int i=0 ; i<5;i++)
{
printf("i = %d\n",i);
}
sleep(2);
return 0;
}
(2)单个线程退出 --pthread_exit
retval指针:必须指向全局,
(3)退出进程的方法:
1、exit(0):退出整个进程
2、pthread_exit(NULL); //退出单个进程
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
void* my_exit()
{
return NULL;
}
void* child_handle(void *arg)
{
printf("child pthread id : %ld\n",pthread_self());
for(int i =0; i<5; i++)
{
printf("child pthread i =%d\n", i);
if( i = 2)
{
//exit child pthread
//exit(0); //退出整个进程
pthread_exit(NULL); //退出单个进程
//my_exit();//通过函数退出
}
}
return 0;
}
int main()
{
pthread_t phid;
int ret;
ret=pthread_create(&phid,NULL,child_handle,NULL);
if( ret ! = 0)
{
printf("error number is %d\n",ret);
printf("%s \n",strerror(ret));
}
printf("parent pthread id : %ld\n",pthread_self());
pthread_exit(NULL);
for(int i=0 ; i<5;i++)
{
printf("parent pthread i= %d\n", i);
}
pthread_exit(NULL);
sleep(2);
return 0;
}
3、阻塞等待线程退出,获取线程退出状态--pthread_join
函数原型:
int pthread_join(pthread_t pthread, void **retval)
参数: pthread:要回收的子线程的ID
retval:读取线程退出的携带信息
传出参数
void* ptr;
pthread_join(pthid,&ptr);
指向的内存和pthread_exit参数指向地址一致
4、线程分离--pthread_detach:
函数原型:int pthread_datach(pthread_t thread);子线程会自动回收自己的PCB
5、杀死(取消)线程--pthread_cancel :
函数原型: int pthread_cancel(pthread_t pthread);
7、线程的分离属性
通过属性设置线程的分离对线程属性变量的初始化int pthread_attr_init(pthread_attr_t* attr);设置线程分离属性int pthread_attr_setdetachstate(pthread_attr_t* attr,int detachstate);参数:attr : 线程属性detachstatePTHREAD_CREATE_DETACHED(分离)PTHREAD_CREATE_JOINABLE(非分离)释放线程资源函数int pthread_attr_destroy(pthread_attr_t* attr);
设置线程分离demo:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
void* child_handle(void *arg)
{
printf("child pthread id : %ld\n",pthread_self());
return 0;
}
int main()
{
pthread_t phid;
int ret;
//init attr
pthread_attr_t attr;
pthread_attr_init(&attr);
//set attr
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
ret=pthread_create(&phid,NULL,child_handle,NULL);
if( ret ! = 0)
{
printf("error number is %d\n",ret);
printf("%s \n",strerror(ret)); //strerror函数返回错误信息是一个字符串
}
printf("parent pthread id : %ld\n",pthread_self());
for(int i=0 ; i<5;i++)
{
printf("i = %d\n",i);
}
sleep(2);
pthread_attr_destroy(&attr);//释放线程资源
return 0;
}
二、线程同步:
为什么要有线程同步?
什么是同步?
协同步调,按照先后顺序操作执行
三、互斥量(互斥锁):
如果我们想要使用互斥锁同步线程所以线程都需要加锁
1.互斥锁类型:
创建一把锁: pthread_mutex_t mutex;
2.互斥锁的特点:多个线程访问共享数据的时候是串行的
3.使用互斥锁缺点 :效率低
4.互斥锁使用的步骤 :
创建互斥锁: pthread_mutex_t mutex;初始化:pthread_mutex_init(&mutex,NULL); -- mutex = 1找到线程共同操作的共享数据加锁:操作共享资源之前加锁,pthread_mutex_lock(&mutex); //阻塞 --mutex = 0pthread_mutex_trylock(&mutex); // 如果锁上锁直接返回,不阻塞XXXXXX共享数据操作 //临界区 ,越小越好解锁: pthread_mutex_unlock(&mutex); // -- mutex = 1阻塞在锁上的线程会被唤醒销毁:pthread_mutex_destory(&mutex);
5.互斥锁相关函数:
初始化互斥锁pthread_mutex_init(pthread_mutex_t* restrict mutex,const pthread_mutexattr_t* restrict attr,);销毁互斥锁:pthread_mutex_destory(pthread_mutex_t* mutex );加锁pthread_mutex_lock(pthread_mutex* mutex);mutex:没有被锁上,当前线程会将这把锁锁上被锁上了:当前线程阻塞,锁被打开之后,线程解除阻塞尝试加锁,失败返回,不阻塞pthread_mutex_trylock(pthread_mutex_t* mutex);没有锁上:当前线程会被这把锁加锁如果锁上了:不会阻塞,返回
if( pthread_mutex_trylock(& mutex)==0)
{
//尝试加锁,并且成功了
//访问共享资源
XXXXXXXX
}
else
{
//错误处理
//或者等待,再次尝试加锁
}
解锁:
pthread_mutex_unlock(pthread_mutex_t* mutex);
使用互斥锁:
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#define MAX 10000
int number=0;
//create mutex
pthread_mutex_t mutex;
//线程A没有将number写到内存中
void* funA(void* arg)
{
for(int i =0; i<MAX; i++)
{
//lock,加锁
pthread_mutex_lock(&mutex);
int ret = number;
ret ++;
number = ret ;
printf(" funA id is %ld ,number is %d\n",pthread_self(),number);
//解锁
pthread_mutex_unlock(&mutex);
usleep(10);
}
}
void* funB(void* arg)
{
for(int i =0; i<MAX; i++)
{
//lock,加锁
pthread_mutex_lock(&mutex);
int ret = number;
ret ++;
number = ret ;
printf(" funB id is %ld ,number is %d\n",pthread_self(),number);
//解锁
pthread_mutex_unlock(&mutex);
usleep(10);
}
}
//结果执行到20000
int main()
{
//init mutex
pthread_mutex_init(&mutex,NULL);
pthread_t pth1,pth2;
pthread_create(&pth1,NULL,funA,NULL);
pthread_create(&pth2,NULL,funB,NULL);
pthread_join(pth1,NULL);
pthread_join(pth2,NULL);
pthread_mutex_destory(&mutex);
return 0;
}
6、原子操作:
void* procucer(void * arg){while(1){// 创建一个链表的节点Node * newNode = (Node*)malloc(sizeof(Node));//initnewNode‐>data = rang()%100;pthread_mutex_lock(&mutex);newNode ‐>next = head;head = newNode;printf("+++product:%d\n",newNode‐>data);pthread_mutex_unlock(&mutex);sleep(rang()%3);}reutn NULL;}
7、死锁:
造成死锁的原因:
1.
自己锁自己
for(int i = 0;i<MAX;i++){pthread_mutex_lock(&mutex);pthread_mutex_lock(&mutex);int crt = number;crt++;number = crt;printf("thread A id = %ld,number = %d\n",pthread_self(),number);pthread_mutex_unlock(&mutex);usleep(10);}//操作做完之后,一定要解锁
2、
线程
1
对共享资源
A
加锁成功
-A
锁
线程
2
对共享资源
B
加锁成功
-B
锁
线程
1
访问共享资源
B
,对
B
锁加锁
-
线程
1
阻塞在
B
锁上
线程
2
访问共享资源
A
,对
A
锁加锁
-
线程
2
阻塞在
A
锁上
如何解决:
-
让线程按照一定的顺序去访问共享资源
-
在访问其他锁的时候,需要先将自己的锁解开
四、读写锁 :
1.读写锁是几把锁?
一把锁pthread_rwlock_t lock;
2.读写锁的类型
读锁-对内存做读操作写锁-对内存做写操作
3、读写锁的特性:
线程A加读锁成功,又来了三个线程,做读操作,可以加锁成功读共享-并行处理线程A加写锁成功,又来了三个线程,做读操作,三个线程阻塞写独占线程A加读锁成功,又来了B线程加写锁阻塞,又来了C线程加读锁阻塞读写不可以同时进行写的优先级高
4、.读写锁场景练习
线程A加写锁成功,线程B请求读锁线程B阻塞线程A持有读锁,线程B请求写锁线程B阻塞线程A拥有读写,线程B请求读锁线程B加锁线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁线程B阻塞,线程C阻塞线程B加锁,线程C阻塞线程C加锁线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁线程B阻塞,线程C阻塞线程C加锁, 线程B阻塞线程B加锁
5、.主要操作函数
初始化读写锁pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,const pthread_rwlockattr_t* restrict attr );销毁读写锁pthread_rwlock_destroy(pthread_rwlock_t* rwlock):加读锁pthread_rwlock_rdlock(pthread_rwlock_t* rdlock);阻塞:之前对这把锁加的是写锁的操作尝试加读锁pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);加锁成功:返回0失败:返回错误号加写锁pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);阻塞:上一次加写锁还没解锁阻塞:上一次加读锁还没解锁尝试加写锁pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);解锁pthread_rwlock_unlock(pthread_rwlock_t* rwlock)
6、读写锁demo:
/*
练习读写锁:
三个线程不定时写同一个全局变量,
五个线程不定时期读同一全局资源
*/
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
int number=1000;
//creat rwlock
pthread_rwlock_t lock;
void* write_fun(void* arg)
{
while(1)
{
//加锁
pthread_rwlock_wrlock(&lock);
number++;
printf("write :%ld ,%d\n",pthread_self(),number);
//解锁
pthread_rwlock_unlock(&lock);
usleep(500); //500ms
}
}
void* read_fun(void* arg)
{
while(1)
{
pthread_rwlock_rdlock(&lock);
printf("read :%ld ,%d\n",pthread_self(),number);
pthread_rwlock_unlock(&lock);
usleep(500);
}
}
int main()
{
pthread_rwlock_init(&lock ,NULL);
pthread_t p[8];
//创建3个写的线程
for (int i =0;i<3;i++)
{
pthread_create(&p[i],NULL,write_fun,NULL);
}
//创建5个读的线程
for (int i =3;i<8;i++)
{
pthread_create(&p[i],NULL,read_fun,NULL);
}
//回收线程
for (int i= 0; i<8; i++)
{
pthread_join(p[i],NULL);
}
//销毁读写锁
pthread_rwlock_destroy(&lock);
return 0;
}
五、条件变量 :
条件变量作用:阻塞线程(不是什么时候都能阻塞线程)
如何才能同步线程?
使用条件变量+互斥锁
1.条件变量是锁吗?
不是锁 ,但是条件变量能够阻塞线程使用条件变量 + 互斥量互斥量:保护一块共享数据条件变量:引起阻塞生产者和消费者模型
2.条件变量的两个动作
条件不满足,阻塞线程当条件满足,通知阻塞的线程开始工作
3.条件变量的类型
pthread_cond_t cond ;
4.主要函数:
初始化一个条件变量pthread_cond_init(pthread_cond_t * restrict cond,const pthread_condattr_t * restrict attr);销毁一个条件变量pthread_cond_destroy(pthread_cond_t * cond);阻塞等待一个条件变量pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t * restrict mutex);阻塞线程将已经上锁的mutex瞬间解锁pthread_cond_timedwait(pthread_cond_t * restrict cond,pthread_mutex_t * restrict mutex,const struct timespec * restrict abstime);唤醒至少一个阻塞在条件变量上的线程pthread_cond_signal(pthread_cond_t* cond);唤醒全部阻塞在条件变量上的线程pthread_cond_broadcast(pthread_cond_t * cond);
5、练习 :使用条件变量实现生产者,消费者模型
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node* next;
}Node_t;
//create head node
Node_t* head = NULL;
//create mutex
pthread_mutex_t mutex;
//create cond
pthread_cond_t cond;
//生产者
void *produce(void *arg)
{
while(1)
{
//create node
Node_t *pnew =(Node_t*)malloc(sizeof(Node_t));
//init node
pnew->data=rand()%1000; //0~999
//lock
pthread_mutex_lock(&mutex);
pnew->next=head;
head=pnew;
printf(" produce(生产者) : %ld ,data is%d\n",pthread_self(),pnew->data);
//unlock
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
sleep(rand()%3);
}
return NULL;
}
//消费者
void *customer(void *arg)
{
while(1)
{
//lock
pthread_mutex_lock(&mutex);
if(head == NULL)
{
//continue;
//阻塞等待
pthread_cond_wait( &cond,&mutex );
}
//delete head node
Node_t *pdel=head;
head =head->next;
printf("customer (消费者): %ld, data is %d\n",pthread_self(),pdel->data);
free(pdel);
//unlock
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_t p1,p2;
pthread_create(&p1,NULL,produce,NULL);
pthread_create(&p2,NULL,customer,NULL);
pthread_join(&p1,NULL);
pthread_join(&p2,NULL);
return 0;
}
六、信号量
1.头文件:#include<semaphore.h>
2、信号量类型:
sem_t sem;
加强版的互斥锁
3.
主要函数 :
初始化信号量sem_init(sem_t *sem,int pshared,unsigned int value);0-线程同步1-进程同步value-最多有几个线程操作共享数据销毁信号量sem_destroy(sem_t *sem);加锁sem_wait(sem_t *sem);如果sem值为0,线程会阻塞尝试加锁sem_trywait(sem_t *sem);sem == 0;加锁失败,不阻塞,直接发牛限时尝试加锁sem_timewait(sem_t *sem,xxxx);解锁++sem_post(sem_t *sem);对sem做了++ 操作
4.练习:使用信号量实现生产者,消费者模型
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<semaphore.h>
#include<unistd.h>
sem_t producer_sem;
sem_t customer_sem;
typedef struct node
{
int data;
struct node* next;
}Node;
Node* head =NULL;
void *producer(void *arg)
{
while(1)
{
sem_wait(&producer_sem); // --- 0
Node* node=(Node*)malloc(sizeof(Node));
node->data=rand()%1000;
node->next=head;
head=node;
printf("producer(生产者): %ld , %d\n",pthread_self(),node->data);
sem_ppost(&customer_sem); //customer++
sleep(rand()%5);
}
return NULL;
}
void *customer(void *arg)
{
while(1)
{
sem_wait(&customer_sem);
Node* del =head;
head=head->next;
printf("customer(消费者): %ld , %d\n",pthread_self(),node->data);
free(del);
sem_ppost(&producer_sem); //producer++
sleep(rand()%5);
}
return NULL;
}
int main()
{
pthread_t thid[2];
sem_init(&producer_sem,0,4);
sem_init(&customer_sem,0,0);
pthread_create(&thid[0],NULL,produce,NULL);
pthread_create(&thid[1],NULL,customer,NULL);
for(int i =0;i <2; i++)
{
pthread_join(thid[i],NULL);
}
sem_destroy(&producer_sem);
sem_destroy(&customer_sem);
return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。