知识点 1 :线程概述
并行与并发的区别:
并发:宏观上看多个任务一起执行(一台咖啡机多人轮流取咖啡)
并行:微观上多个任务一起进行(两台咖啡机两人同时取咖啡) 需要多个核
可以理解为:在Linux中线程就是进程(类似的)
线程也是完成并发的多个任务(可以相同可以不同),一般来说是相同的 轻量级(共享)
进程也是完成并发的多个任务(可以相同可以不同) 重量级(不共享)
ps -Lf pid 可以查看该pid下的所有线程 PID 进程号 LWP 线程号
知识点 2 :线程和进程的区别
通过子进程可以fork创建出子进程,会复制父进程的虚拟地址空间,父子进程之间的共享(进程间通信)困难。线程间的信息是可以共享的。
代价较高:复制父进程的虚拟地址空间(时间)
因为线程间是可以共享数据的
因为线程间是共享虚拟地址空间的,也就是共享内存,不需要复制,速度更快
知识点 3 :线程和进程的虚拟地址空间
进程:写时复制,读时共享
线程: 都是共享的,除了栈空间和text代码段会分成线程的几个部分,栈空间可以分成主线程和子线程。
知识点 4 :线程之间共享和非共享资源
其实共享的资源就是内核的资源共享
每个线程的ID不同肯定是不共享的
知识点 5 :NPTL(了解即可)
知识点 6 :线程操作API man pthread
1 查看pthread所有功能函数 man pthread
2 创建线程 pthread_create
一般情况下,main函数所在的线程称为主线程(main线程),其余创建的是子线程
如果程序中默认一个进程,fork之后会产生一个子进程,那么此时有两个进程
如果程序中默认一个线程,pthread_create之后会产生一个子线程,那么此时有两个线程
第一个参数:线程ID类型的指针 :创建tid的地址
第二个参数:属性:NULL(自己不设置)
第三个参数:逻辑代码:通过函数指针实现 callback回调
callback 返回void* 穿的参数也是void *
第四个参数:不需要传递参数就用 NULL 也可以传递参数(后面有例子)
pthread_create 返回int 类型
void * 万能指针
错误号 strerror(错误号)返回字符串char * 类型
案例:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
//子线程执行的代码
void* callback(void *arg)
{
printf("子线程\n");
return NULL;
}
int main()
{
pthread_t tid;
//创建一个子线程
int ret = pthread_creat(&tid, NULL.callback, NULL);
if (ret == 0)
{
//创建子线程成功
printf("子线程创建成功");
}
else
{
//创建子线程失败并报错返回错误号
char * errstr = strerror(ret);
printf("error : %s\n", errstr);
}
for (int i = 0; i < 5; i++)
{
printf(% d\n, i);
}
return 0;// 相当于exit(0) 退出进程
}
因为线程他不是系统自带的库,是第三方库,所以报错,应该指定库名pthread libpthread.so
也可以通过-pthread进行编译和链接
运行结果:发现子线程里面的内容没有执行:
因为main函数里面所有的内容都是主线程,callback是子线程,只要主线程里面有return 0 一执行就退出了线程,不再执行子线程
解决:return 0 前面加sleep (1); 加unix头文件 后面还有更好的方法 pthread_exit
拓展:如果想要创建多个子线程,可以多次调用pthread_create callback可以一样也可以内容不同
对于第四个参数传递其他参数的情形:void *类型指针
如果 int num=10 传入参数强转 (void * )&num
那么这个时候第四个参数num就传递给了callback的void *arg部分,要想获得arg的值,本来是void *类型强转成int * 最后再用*取他的值
![](https://img-blog.csdnimg.cn/direct/2874b5828eee4c6589b2a08f7b57d3ea.png)
3 终止线程 pthread_exit
4 获取子线程ID pthread_self
案例:看看子线程打印的子线程ID和主线程打印的子线程ID是否相同
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void* callback(void* arg)
{
//获取子线程id
printf("子线程tid: %ld\n",pthread_self());
}
int main()
{
pthread_t tid;
//创建子线程
int ret= pthread_create(&tid, NULL, callback, NULL);
if (ret == 0)
{
//子线程创建成功
printf("子线程创建成功!\n");
}
else
{
//子线程创建失败并返回错误号
char * errstr = strerror(ret);
printf("error: %s\n", errstr);
}
//主线程
for (int i = 0; i < 5; i++)
{
printf("%d\n", i);
}
//比较主线程中子线程的id和子线程中子线程id是否相同
printf("子线程id: %ld\n, 主线程的子线程id:%ld\n ", tid,pthread_self());
//主线程终止
pthread_exit(NULL);
return 0;
}
运行结果:
如果i更大一点,循环次数更多一点,就会出现主线程子线程交替运行的情况
若加上
打印不出来,主线程退出之后后面的代码就不再执行了 exit也不再执行,不影响子线程
5 比较线程号是否相等 pthread_equal
yi线程号是长整型,为啥不用==直接判断,而用pthread_equal函数?
答:不同的操作系统,pthread_t类型的实现不同,有的是无符号的长整型(大多),有的是用结构体实现的(那比较的是地址就不可用==),跨平台一定要使用pthread_equal
6 连接已终止的线程 pthread_join
注意:任何的线程都可以去回收已经终止的线程(主线程回收子线程的数据)
连接:回收(如果多个子线程运行完不回收就会出现僵尸线程)
和wait函数一样 阻塞
多次回收:多次调用join
**retval
1 二级指针:*retval就已经能指向这个值了,为什么用二级指针?
2 接收子线程退出exit时的返回值
可以传递NULL 也可以是其他值(void*类型)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void* callback(void* arg)
{
//获取子线程id
printf("子线程tid: %ld\n",pthread_self());
return(NULL);
}
int main()
{
pthread_t tid;
//创建子线程
int ret= pthread_create(&tid, NULL, callback, NULL);
if (ret == 0)
{
//子线程创建成功
printf("子线程创建成功!\n");
}
else
{
//子线程创建失败并返回错误号
char * errstr = strerror(ret);
printf("error: %s\n", errstr);
}
//主线程
for (int i = 0; i < 5; i++)
{
printf("%d\n", i);
}
//比较主线程中子线程的id和子线程中子线程id是否相同
printf("子线程id: %ld\n, 主线程的子线程id:%ld\n ", tid,pthread_self());
//主线程调用pthread_join回收子线程的资源
pthread_join(tid,NULL); //不想要返回什么值传递NULL
//主线程终止
pthread_exit(NULL);
return 0;
}
无法看出是否回收资源
若加上:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void* callback(void* arg)
{
//获取子线程id
printf("子线程tid: %ld\n", pthread_self());
sleep(3);//unistd.h 想让子线程晚点结束
return(NULL);
}
int main()
{
pthread_t tid;
//创建子线程
int ret = pthread_create(&tid, NULL, callback, NULL);
if (ret == 0)
{
//子线程创建成功
printf("子线程创建成功!\n");
}
else
{
//子线程创建失败并返回错误号
char* errstr = strerror(ret);
printf("error: %s\n", errstr);
}
//主线程
for (int i = 0; i < 5; i++)
{
printf("%d\n", i);
}
//比较主线程中子线程的id和子线程中子线程id是否相同
printf("子线程id: %ld\n, 主线程的子线程id:%ld\n ", tid, pthread_self());
//主线程调用pthread_join回收子线程的资源
int ret = pthread_join(tid, NULL); //不想要返回什么值传递NULL 只要子线程没有结束,就不会回收
if (ret == 0)
{
//回收成功
printf("回收子线程资源成功!\n");
}
else
{
//回收失败并返回错误号
char* errstr = strerror(ret);
printf("error: %s\n", errstr);
}
//主线程终止
pthread_exit(NULL);
return 0;
}
阻塞了,等待三秒(等待子线程结束了,才能继续往下运行)
若更改子线程的内容:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void* callback(void* arg)
{
//获取子线程id
printf("子线程tid: %ld\n", pthread_self());
int value = 10;
pthread_exit((void *) & value)//内部参数void *retval value是int类型数值,取地址变int类型指针&value 强转
}
int main()
{
pthread_t tid;
//创建子线程
int ret = pthread_create(&tid, NULL, callback, NULL);
if (ret == 0)
{
//子线程创建成功
printf("子线程创建成功!\n");
}
else
{
//子线程创建失败并返回错误号
char* errstr = strerror(ret);
printf("error: %s\n", errstr);
}
//主线程
for (int i = 0; i < 5; i++)
{
printf("%d\n", i);
}
//比较主线程中子线程的id和子线程中子线程id是否相同
printf("子线程id: %ld\n, 主线程的子线程id:%ld\n ", tid, pthread_self());
int * retval;
//主线程调用pthread_join回收子线程的资源
int ret = pthread_join(tid, void(**) & retval); //第二个参数为二级指针,所以取地址 转**
if (ret == 0)
{
//回收成功
printf("回收子线程资源成功!\n");
}
else
{
//回收失败并返回错误号
char* errstr = strerror(ret);
printf("error: %s\n", errstr);
}
printf("返回数据:%d\n", *retval);//返回的是int类型,就是自己的定义
//主线程终止
pthread_exit(NULL);
return 0;
}
返回的数值是一个随机的数值(每一次都不一样),和value不一样(因为value是一个局部变量,子线程执行完之后有自己的栈(主栈和子栈),子线程运行完之后,栈空间释放,就获取不到了)
所以需要定义一个全局变量,都是共享的
7 线程的分离 pthread_detach
设置
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg) {
printf("chid thread id : %ld\n", pthread_self());
return NULL;
}
int main() {
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error1 : %s\n", errstr);
}
// 输出主线程和子线程的id
printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
// 设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放
ret = pthread_detach(tid);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error2 : %s\n", errstr);
}
// 设置分离后,对分离的子线程进行连接 pthread_join() 不能连接一个已经分离的线程
// ret = pthread_join(tid, NULL);
// if(ret != 0) {
// char * errstr = strerror(ret);
// printf("error3 : %s\n", errstr);
// }
pthread_exit(NULL);
return 0;
}
子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放了
如果将一个分离的线程连接,会报错:非法参数
8 线程取消 pthread_cancel
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg) {
printf("chid thread id : %ld\n", pthread_self());
for(int i = 0; i < 5; i++) {
printf("child : %d\n", i); //相当于一个取消点
}
return NULL;
}
int main() {
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error1 : %s\n", errstr);
}
// 取消线程 终止子线程
pthread_cancel(tid);
for(int i = 0; i < 5; i++) {
printf("%d\n", i);
}
// 输出主线程和子线程的id
printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
pthread_exit(NULL);
return 0;
}
多次调用,结果可能不同
注意:线程取消并不是立马终止
知识点 7 :线程属性
1 查看与线程属性相关的函数 man phtread_attr_
2 设置线程分离的状态属性 pthread_attr_setdetachstate
之前有利用过pthread_detach的方法使得线程分离
线程分离的作用:线程分离之后,子线程结束之后,不需要主线程进行资源回收
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg) {
printf("chid thread id : %ld\n", pthread_self());
return NULL;
}
int main() {
// 创建一个线程属性变量
pthread_attr_t attr;
// 初始化属性变量
pthread_attr_init(&attr);
// 设置属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, &attr, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error1 : %s\n", errstr);
}
// 输出主线程和子线程的id
printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
// 释放线程属性资源
pthread_attr_destroy(&attr);
pthread_exit(NULL);
return 0;
}
运行结果:无法看出来有没有回收
可以通过栈的大小观察得到:
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg) {
printf("chid thread id : %ld\n", pthread_self());
return NULL;
}
int main() {
// 创建一个线程属性变量
pthread_attr_t attr;
// 初始化属性变量
pthread_attr_init(&attr);
// 设置属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, &attr, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error1 : %s\n", errstr);
}
// 获取线程的栈的大小
size_t size;
pthread_attr_getstacksize(&attr, &size);
printf("thread stack size : %ld\n", size)//必需ld
// 输出主线程和子线程的id
printf("tid : %ld, main thread id : %ld\n", tid, pthread_self());
// 释放线程属性资源
pthread_attr_destroy(&attr);
pthread_exit(NULL);
return 0;
}
默认栈的大小:8388608
知识点 8 :线程同步
案例:多线程实现买票(3个窗口总共100张票)
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void* sellticket(void* arg)
{
int tickets = 100;
while (tickets > 0)
{
//有票
printf("%ld正在出售第 %d 张票\n", pthread_self(), tickets);
ticket--;
}
return NULL;
}
int main()
{
//创建3个子线程(3个窗口)
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, sellticket, NULL);
pthread_create(&tid2, NULL, sellticket, NULL);
pthread_create(&tid3, NULL, sellticket, NULL);
//回收子线程 阻塞
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
//退出主线程
pthread_exit(NULL);
return 0;
}
运行结果:
子线程112:100-80
子线程408:100-1
子线程704:100-26
子线程112:79-1
子线程704:25-1
但是我们的要求是总共100张,这里是每个窗口都卖100张,说明代码逻辑有问题
解决:
tickets改为全局变量
运行还是发现问题:三个线程卖了同一张门票
为了看的更明显:usleep(6000)每个线程都卖了同一张票 且0 -1
原因:
卖同一张门票:A线程执行之后休眠,BC线程可能抢占资源也运行了
-1:AB线程都进入程序中,A先执行到0了,B也要运行就变成-1
解决方法:线程同步:每次都只有一个线程取操作这块共享资源
原子操作:不可分割,最小的,一口气都执行完,中间不可以有其他线程进入
线程同步:主要是针对临界区(保证安全性)
知识点 9 :互斥锁
当线程A拿到锁M,那么线程B就阻塞了。如果线程B拿到锁了,那么会锁死,线程B就拿不到了
知识点 10 :互斥锁相关操作函数
初始化init 销毁destroy lock 锁定 trylock 尝试锁定 unlock 解锁
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
//共享同一份数据
int tickets = 100;
//创建一个互斥量(全局变量)
pthread_mutex_t mutex;
//这样就能实现A线程执行会加锁,BC线程无法加入除非A线程解锁
void* sellticket(void* arg)
{
//加锁
pthread_mutex_lock(&mutex);
while (tickets > 0) //产生问题的临界区
{
//有票
printf("%ld正在出售第 %d 张票\n", pthread_self(), tickets);
ticket--;
}
//解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
//创建一个互斥量(不在main函数里面创建,不然是局部变量,结束之后就被清除了,所以应该是全局变量)
//初始化互斥量
pthread_mutex_Init(&mutex);
//创建3个子线程(3个窗口)
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, sellticket, NULL);
pthread_create(&tid2, NULL, sellticket, NULL);
pthread_create(&tid3, NULL, sellticket, NULL);
//回收子线程 阻塞
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
//退出主线程
pthread_exit(NULL);
//释放互斥量
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果:都是由同一个线程来全部完成了(while循环的问题)
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
//共享同一份数据
int tickets = 1000;
//创建一个互斥量(全局变量)
pthread_mutex_t mutex;
//这样就能实现A线程执行会加锁,BC线程无法加入除非A线程解锁
void* sellticket(void* arg)
{
while (1) //产生问题的临界区
{
if (tickets > 0)
{
//加锁
pthread_mutex_lock(&mutex);
//有票
printf("%ld正在出售第 %d 张票\n", pthread_self(), tickets);
ticket--;
}
else
{
printf("卖完了!");
break;
//解锁
pthread_mutex_unlock(&mutex);
}
//解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
//创建一个互斥量(不在main函数里面创建,不然是局部变量,结束之后就被清除了,所以应该是全局变量)
//初始化互斥量
pthread_mutex_Init(&mutex);
//创建3个子线程(3个窗口)
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, sellticket, NULL);
pthread_create(&tid2, NULL, sellticket, NULL);
pthread_create(&tid3, NULL, sellticket, NULL);
//回收子线程 阻塞
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
//退出主线程
pthread_exit(NULL);
//释放互斥量
pthread_mutex_destroy(&mutex);
return 0;
}
tickets的值要大一点 不然运行速度太快,都是一个线程完成的
知识点 11 :死锁
类型一:重复加锁 阻塞
类型二:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 创建2个互斥量
pthread_mutex_t mutex1, mutex2;
void * workA(void * arg) {
pthread_mutex_lock(&mutex1);
sleep(1);
pthread_mutex_lock(&mutex2);
printf("workA....\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return NULL;
}
void * workB(void * arg) {
pthread_mutex_lock(&mutex2);
sleep(1);
pthread_mutex_lock(&mutex1);
printf("workB....\n");
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return NULL;
}
int main() {
// 初始化互斥量
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
// 创建2个子线程
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, workA, NULL);
pthread_create(&tid2, NULL, workB, NULL);
// 回收子线程资源
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
// 释放互斥量资源
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
知识点 12 :读写锁
1 概念
2 读写锁相关操作函数
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 创建一个共享数据
int num = 1;
// pthread_mutex_t mutex;
pthread_rwlock_t rwlock;
void * writeNum(void * arg) {
while(1) {
pthread_rwlock_wrlock(&rwlock);
num++;
printf("++write, tid : %ld, num : %d\n", pthread_self(), num);
pthread_rwlock_unlock(&rwlock);
usleep(100);
}
return NULL;
}
void * readNum(void * arg) {
while(1) {
pthread_rwlock_rdlock(&rwlock);
printf("===read, tid : %ld, num : %d\n", pthread_self(), num);
pthread_rwlock_unlock(&rwlock);
usleep(100);
}
return NULL;
}
int main() {
pthread_rwlock_init(&rwlock, NULL);
// 创建3个写线程,5个读线程
pthread_t wtids[3], rtids[5];
for(int i = 0; i < 3; i++) {
pthread_create(&wtids[i], NULL, writeNum, NULL);
}
for(int i = 0; i < 5; i++) {
pthread_create(&rtids[i], NULL, readNum, NULL);
}
// 设置线程分离
for(int i = 0; i < 3; i++) {
pthread_detach(wtids[i]);
}
for(int i = 0; i < 5; i++) {
pthread_detach(rtids[i]);
}
pthread_exit(NULL);
pthread_rwlock_destroy(&rwlock);
return 0;
}
知识点 13 :生产者消费者模型
生产者:负责生产商品
生产者发现容器满了需要通知消费者
消费者发现容器空了需要通知生产者
实际生活中不止一个生产者一个消费者
容器相当于是一份共享的数据,生产者和消费者相当于线程,多个线程同时对一份数据进行操作,肯定会产生数据安全问题。
/*
生产者消费者模型(粗略的版本)
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
// 创建一个互斥量
pthread_mutex_t mutex;
struct Node{
int num;
struct Node *next;
};
// 头结点
struct Node * head = NULL;
void * producer(void * arg) {
// 不断的创建新的节点,添加到链表中
while(1) {
pthread_mutex_lock(&mutex);
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->next = head;
head = newNode;
newNode->num = rand() % 1000;
printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
pthread_mutex_unlock(&mutex);
usleep(100);
}
return NULL;
}
void * customer(void * arg) {
while(1) {
pthread_mutex_lock(&mutex);
// 保存头结点的指针
struct Node * tmp = head;
// 判断是否有数据
if(head != NULL) {
// 有数据
head = head->next;
printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);
usleep(100);
} else {
// 没有数据
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main() {
pthread_mutex_init(&mutex, NULL);
// 创建5个生产者线程,和5个消费者线程
pthread_t ptids[5], ctids[5];
for(int i = 0; i < 5; i++) {
pthread_create(&ptids[i], NULL, producer, NULL);
pthread_create(&ctids[i], NULL, customer, NULL);
}
for(int i = 0; i < 5; i++) {
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
while(1) {
sleep(10);
}
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
return 0;
}
知识点 14 : 条件变量
条件变量不是锁,但是可以引起线程阻塞(某个条件满足之后阻塞或者某个条件满足之后解除阻塞)
条件变量只是配合互斥量一起使用,共同解决线程同步的问题
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
// 创建一个互斥量
pthread_mutex_t mutex;
// 创建条件变量
pthread_cond_t cond;
struct Node{
int num;
struct Node *next;
};
// 头结点
struct Node * head = NULL;
void * producer(void * arg) {
// 不断的创建新的节点,添加到链表中
while(1) {
pthread_mutex_lock(&mutex);
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->next = head;
head = newNode;
newNode->num = rand() % 1000;
printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
// 只要生产了一个,就通知消费者消费
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
usleep(100);
}
return NULL;
}
void * customer(void * arg) {
while(1) {
pthread_mutex_lock(&mutex);
// 保存头结点的指针
struct Node * tmp = head;
// 判断是否有数据
if(head != NULL) {
// 有数据
head = head->next;
printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
free(tmp);
pthread_mutex_unlock(&mutex);
usleep(100);
} else {
// 没有数据,需要等待
// 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main() {
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
// 创建5个生产者线程,和5个消费者线程
pthread_t ptids[5], ctids[5];
for(int i = 0; i < 5; i++) {
pthread_create(&ptids[i], NULL, producer, NULL);
pthread_create(&ctids[i], NULL, customer, NULL);
}
for(int i = 0; i < 5; i++) {
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
while(1) {
sleep(10);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
pthread_exit(NULL);
return 0;
}
知识点 15 :信号量
现在