概述
目录
- 作者介绍
- 学习目标
- 1.互斥锁的使用步骤
- 2 死锁
- 2.1 死锁的产生原因
- 2.2 死锁如何解决
- 3. 读写锁(是一把锁,读时共享写时独占)读写互斥
- 3.1读写锁的函数
- 3.2 读写锁的使用步骤
- 3.3 读写锁的代码案例
- 4. 介绍生产者和消费者模型
- 4.1 链表操作
- 4.2 条件变量(与互斥锁配合使用,给多线程提供一个回合的场所)
- 4.3 条件变量相关函数
- 4.4 使用条件变量实现生产者和消费者模型
- 4.5 条件变量使用总结
- 5.信号量(相当于加强版的互斥锁)
- 5.1 信号量相关函数(互斥锁加条件变量)
- 5.2 信号量实现生产者与消费者
- 5.3 总结
- ---------回顾linux系统编程课程------------
作者介绍
张伟伟,男,西安工程大学电子信息学院,2019级硕士研究生,张宏伟人工智能课题组。
微信公众号:可随时查阅,搜索—张二牛的笔记,内容会分类上传。
研究方向:机器视觉与人工智能。
电子邮件:2430290933@qq.com
电子邮件:zhangweiweicpp@163.com
- 课题组CSDN官方账号,欢迎一键三连: https://blog.csdn.net/m0_37758063/article/details/113527955?spm=1001.2014.3001.5501.
学习目标
- 熟练掌握互斥量的使用
- 说出什么叫死锁以及解决方案
- 熟练掌握读写锁的使用
- 熟练掌握**条件变量**的使用
- 理解条件变量实现的生产消费者模型
- 理解信号量实现的生产消费者模型
1.互斥锁的使用步骤
1. 创建一把互斥锁
pthread_mutex_t mutex;
2. 初始化互斥锁
pthread_mutex_init(&mutex);---相当于mutex=1
3. 在代码中寻找共享资源(也称为临界区)
pthread_mutex_lock(&mutex); -- mutex = 0[临界区代码]
4. pthread_mutex_unlock(&mutex); -- mutex = 1
5. 释放互斥锁资源
pthread_mutex_destroy(&mutex);
2 死锁
2.1 死锁的产生原因
死锁并不是linux提供给用户的一种使用方法,而是由于用户使用互斥锁不当引起的一种现象。
1.自己锁自己 :注意点:线程在异常退出时也需要解锁
2.加锁不释放
2.2 死锁如何解决
不同时两把锁,线程在异常退出时也需要解锁。
让线程按照一定的顺序去访问共享资源
1.在访问其他锁的时候,需要先将自己的锁解开
2.避免使用嵌套的锁,让线程按照一定的顺序去加锁
3.调用pthread_mutex_trylock,如果加锁不成功会立刻返回
3. 读写锁(是一把锁,读时共享写时独占)读写互斥
什么是读写锁
读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。
当读写同时等待锁的时候写的优先级高
情景:
线程A拥有读锁, 线程B请求读锁
3.1读写锁的函数
定义一把读写锁
pthread_rwlock_t rwlock;
初始化读写锁
int pthread_rwlock_init(
pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
函数参数
rwlock-读写锁
attr-读写锁属性,传NULL为默认属性
销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
尝试加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
解锁
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);
3.2 读写锁的使用步骤
1.先定义一把锁:
pthread_rwlock_t rwlock;
2.初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
----子线程------
3.加锁
pthread_rwlock_rdlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
共享资源出现的位置
4.解锁
pthread_rwlock_unlock(&rwlock);
---子线程-------
5.释放锁
pthread_rwlock_destroy(&rwlock);
3.3 读写锁的代码案例
- 线程回调函数传传参方法。
//读写锁测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
int number = 0;
//定义一把读写锁
pthread_rwlock_t rwlock;
//写线程回调函数
void *thread_write(void *arg)
{
int i = *(int *)arg;
int cur;
while(1)
{
//加写锁
pthread_rwlock_wrlock(&rwlock);
cur = number;
cur++;
number = cur;
printf("[%d]-W:[%d]n", i, cur);
//解锁
pthread_rwlock_unlock(&rwlock);
sleep(rand()%3);
}
}
//读线程回调函数
void *thread_read(void *arg)
{
int i = *(int *)arg;
int cur;
while(1)
{
//加读锁
pthread_rwlock_rdlock(&rwlock);
cur = number;
printf("[%d]-R:[%d]n", i, cur);
//解锁
pthread_rwlock_unlock(&rwlock);
sleep(rand()%3);
}
}
int main()
{
int n = 8;
int i = 0;
int arr[8];
pthread_t thread[8];
//读写锁初始化
pthread_rwlock_init(&rwlock, NULL);
//创建3个写子线程
for(i=0; i<3; i++)
{
arr[i] = i;
pthread_create(&thread[i], NULL, thread_write, &arr[i]);
}
//创建5个读子线程
for(i=3; i<n; i++)
{
arr[i] = i;
pthread_create(&thread[i], NULL, thread_read, &arr[i]);
}
//回收子线程
int j = 0;
for(j=0;j<n; j++)
{
pthread_join(thread[j], NULL);
}
//释放锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
4. 介绍生产者和消费者模型
4.1 链表操作
链表的使用
节点,指向,偏移
头插法,移动,删除,head为共享资源。
防止循环空转: 阻塞等待,解锁,然后加锁。
生产者线程 | 消费者线程
4.2 条件变量(与互斥锁配合使用,给多线程提供一个回合的场所)
pthread_cond_wait()
pthread_cond_signal()
(1) 条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
(2) 使用互斥量保护共享数据;
(3) 使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞.
(4) 条件变量的两个动作:
---------条件不满足, 阻塞线程
---------条件满足, 通知阻塞的线程解除阻塞, 开始工作
4.3 条件变量相关函数
pthread_cond_t cond;
定义一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
函数描述:初始化条件变量
函数参数:
cond: 条件变量
attr: 条件变量属性, 通常传NULL
函数返回值:成功返回0, 失败返回错误号
int pthread_cond_destroy(pthread_cond_t *cond);
函数描述: 销毁条件变量
函数参数: 条件变量
返回值: 成功返回0, 失败返回错误号
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
函数描述: 条件不满足, 引起线程阻塞并解锁;
条件满足, 解除线程阻塞, 并加锁
函数参数:
cond: 条件变量
mutex: 互斥锁变量
函数返回值: 成功返回0, 失败返回错误号
int pthread_cond_signal(pthread_cond_t *cond);
函数描述: 唤醒至少一个阻塞在该条件变量上的线程
函数参数: 条件变量
函数返回值: 成功返回0, 失败返回错误号
4.4 使用条件变量实现生产者和消费者模型
//使用条件变量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *head = NULL;
//定义一把锁
pthread_mutex_t mutex;
//定义条件变量
pthread_cond_t cond;
//生产者线程
void *producer(void *arg)
{
NODE *pNode = NULL;
int n = *(int *)arg;
while(1)
{
//生产一个节点
pNode = (NODE *)malloc(sizeof(NODE));
if(pNode==NULL)
{
perror("malloc error");
exit(-1);
}
pNode->data = rand()%1000;
printf("P[%d]:[%d]n", n, pNode->data);
//加锁
pthread_mutex_lock(&mutex);
pNode->next = head;
head = pNode;
//解锁
pthread_mutex_unlock(&mutex);
//通知消费者线程解除阻塞
pthread_cond_signal(&cond);
sleep(rand()%3);
}
}
//消费者线程
void *consumer(void *arg)
{
NODE *pNode = NULL;
int n = *(int *)arg;
while(1)
{
//加锁
pthread_mutex_lock(&mutex);
if(head==NULL)
{
//若条件不满足,需要阻塞等待
//若条件不满足,则阻塞等待并解锁;
//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
pthread_cond_wait(&cond, &mutex);
}
if(head==NULL)
{
//解锁
pthread_mutex_unlock(&mutex);
continue;
}
printf("C[%d]:[%d]n", n, head->data);
pNode = head;
head = head->next;
//解锁
pthread_mutex_unlock(&mutex);
free(pNode);
pNode = NULL;
sleep(rand()%3);
}
}
int main()
{
int ret;
int i = 0;
pthread_t thread1[5];
pthread_t thread2[5];
//初始化互斥锁
pthread_mutex_init(&mutex, NULL);
//条件变量初始化
pthread_cond_init(&cond, NULL);
int arr[5];
for(i=0; i<5; i++)
{
arr[i]= i;
//创建生产者线程
ret = pthread_create(&thread1[i], NULL, producer, &arr[i]);
if(ret!=0)
{
printf("pthread_create error, [%s]n", strerror(ret));
return -1;
}
//创建消费者线程
ret = pthread_create(&thread2[i], NULL, consumer, &arr[i]);
if(ret!=0)
{
printf("pthread_create error, [%s]n", strerror(ret));
return -1;
}
}
//等待线程结束
for(i=0; i<5; i++)
{
pthread_join(thread1[i], NULL);
pthread_join(thread2[i], NULL);
}
//释放互斥锁
pthread_mutex_destroy(&mutex);
//释放条件变量
pthread_cond_destroy(&cond);
return 0;
}
4.5 条件变量使用总结
1、定义条件变量
pthread_cont_t cond;
2.初始化条件变量
pthread_cond_init(&cond, NULL);
3.在生产者线程中调用:
pthread_cond_signal(&cond);
4.在消费者线程中调用:
pthread_cond_desroy(&cond);
5.主线程释放条件变量
多个生产者和多个消费者程序在执行的时候core掉 的原因分析:
假若只有一个生产者生产了一个节点,此时会调用pthread_cond_signal通知消费者线程,此时若有多个消费者线程被唤醒了,则最终只有一个消费者获得锁,然后进行消费,此时将head置位NULL,然后其余的几个消费者线程只会有一个线程获得锁,然后读取head的内容就会core掉。
在使用条件变量的线程中,能够引起线程阻塞的地方有两个:
1.在条件变量出引起阻塞,这个阻塞会被pthread_cond_signal解除阻塞。
2.互斥锁也会使线程引起阻塞,其他县城解锁会使该线程解除阻塞。
5.信号量(相当于加强版的互斥锁)
互斥锁:进一个出一个
信号量:多个同时
车库为共享资源 车为线程 管理员相当于锁
5.1 信号量相关函数(互斥锁加条件变量)
定义信号量 sem_t sem;
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数描述: 初始化信号量
函数参数:
sem: 信号量变量
pshared: 0表示线程同步, 1表示进程同步
value: 最多有几个线程操作共享数据
函数返回值:成功返回0, 失败返回-1, 并设置errno值
int sem_wait(sem_t *sem); **(加锁)**
函数描述: 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值
int sem_post(sem_t *sem); **(解锁)**
函数描述: 调用一次, 相当于sem++
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值
int sem_trywait(sem_t *sem);
函数描述: 尝试加锁, 若失败直接返回, 不阻塞
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值
int sem_destroy(sem_t *sem);
函数描述: 销毁信号量
函数参数: 信号量变量
函数返回值: 成功返回0, 失败返回-1, 并设置errno值
5.2 信号量实现生产者与消费者
//使用信号量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node
{
int data;
struct node *next;
}NODE;
NODE *head = NULL;
//定义信号量
sem_t sem_producer;
sem_t sem_consumer;
//生产者线程
void *producer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//生产一个节点
pNode = (NODE *)malloc(sizeof(NODE));
if(pNode==NULL)
{
perror("malloc error");
exit(-1);
}
pNode->data = rand()%1000;
printf("P:[%d]n", pNode->data);
//加锁
sem_wait(&sem_producer); //--
pNode->next = head;
head = pNode;
//解锁
sem_post(&sem_consumer); //相当于++
sleep(rand()%3);
}
}
//消费者线程
void *consumer(void *arg)
{
NODE *pNode = NULL;
while(1)
{
//加锁
sem_wait(&sem_consumer); //相当于--
printf("C:[%d]n", head->data);
pNode = head;
head = head->next;
//解锁
sem_post(&sem_producer); //相当于++
free(pNode);
pNode = NULL;
sleep(rand()%3);
}
}
int main()
{
int ret;
pthread_t thread1;
pthread_t thread2;
//初始化信号量
sem_init(&sem_producer, 0, 5);
sem_init(&sem_consumer, 0, 0);
//创建生产者线程
ret = pthread_create(&thread1, NULL, producer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]n", strerror(ret));
return -1;
}
//创建消费者线程
ret = pthread_create(&thread2, NULL, consumer, NULL);
if(ret!=0)
{
printf("pthread_create error, [%s]n", strerror(ret));
return -1;
}
//等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
//释放信号量资源
sem_destroy(&sem_producer);
sem_destroy(&sem_consumer);
return 0;
}
5.3 总结
1. 定义信号量变量
sem_t sem1;
sem_t sem2;
2. 初始化信号量
sem_init(&sem1, 0, 5);
sem_init(&sem2, 0, 5);
3. 加锁
sem_wait(&sem1);
sem_post(&sem2);
sem_wait(&sem2);
sem_post(&sem1);
4. 释放资源
sem_destroy(&sem1);
sem_destroy(&sem2);
---------回顾linux系统编程课程------------
1. linux常用命令
linux 目录结构一级常见目录的作用
ls cp mv rm rmdir mkdir touch chmod chgrp chown which whoami tar zip rar find grep
2. vim的使用:命令模式 编辑模式 末行模式
gcc的工作流程
库的制作与使用
动态库的加载的时候报错问题的处理:export LD_LIBRAY_PATH=$LD_LIBRARY_PATH:./
3.makefile 和 gdb 调试
目标:依赖
命令
函数 变量 伪目标
编译的时候加-g ,否则不能用gdb调试
理解什么是文件描述符
4.文件IO
open read write close lseek
lstat stat
dup dup2 fcntl
opendir readdir closedir
5.进程相关
重在理解进程和程序的概念
fork函数
execl execlp函数
父进程中调用wait和waitpid,参数和返回值。
6.进程间通信
pipe fifo mmap
7.信号
信号的基本概念
信号的处理机制:内核
信号的特点
信号的四要素
信号相关函数: kill alarm settimer raise abort
信号注册函数:signal sigaction(高速内核)
信号的处理过程:
SIGCHLD信号:该信号产生的条件及作用(告诉内核回收子进程)**回顾linux系统编程课程**
信号集相关:
阻塞信号集合未决信号集的关系
信号不支持排队
sigset_t set
sigemptyset sigaddset sigfillset sigelset sigprocmask sigsmember sigpending
8.守护进程与线程
创建守护进程的模型:(代码看看)
线程:
线程的基本概念
线程和进程的比较(共享哪些,不能够共享哪些)
线程相关函数:
pthread_create
pthread_exit
pthread_join
pthread_detach
pthread_cancel
pthread_testcancel
9.线程同步:
互斥锁的使用步骤
读写锁的使用步骤
条件变量的使用步骤
最后
以上就是活泼嚓茶为你收集整理的Linux系统编程09---线程同步作者介绍学习目标1.互斥锁的使用步骤2 死锁3. 读写锁(是一把锁,读时共享写时独占)读写互斥4. 介绍生产者和消费者模型5.信号量(相当于加强版的互斥锁)---------回顾linux系统编程课程------------的全部内容,希望文章能够帮你解决Linux系统编程09---线程同步作者介绍学习目标1.互斥锁的使用步骤2 死锁3. 读写锁(是一把锁,读时共享写时独占)读写互斥4. 介绍生产者和消费者模型5.信号量(相当于加强版的互斥锁)---------回顾linux系统编程课程------------所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复