2019年8月15日星期四(系统编程)

2019年8月15日星期四

一. 线程互斥方式 - 互斥锁。

1. 什么是互斥锁?特点如何?

互斥锁是专门用于处理线程互斥的一个方式,它有两种状态:上锁状态/解锁状态。

特点:如果处理上锁状态,则不能再上锁,直到解锁为止才能再上锁。如果是处于解锁状态,则不能再解锁了,直到上锁了才能再解锁。

2. 关于线程互斥锁API函数接口?

0)定义互斥锁的变量(pthread_mutex_t-> 互斥锁的数据类型)

pthread_mutex_t mutex;

1)初始化互斥锁  -> pthread_mutex_init()  -> man 3 pthread_mutex_init

功能: initialize a mutex  -> 初始化互斥锁

使用格式:

#include <pthread.h>

-> 动态初始化

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);

mutex:  互斥锁的地址

attr:   互斥锁的属性,一般填NULL

返回值:

成功:0

失败:错误码

-> 静态初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2)上锁  -> pthread_mutex_lock() -> man 3 pthread_mutex_lock

功能:lock a mutex

使用格式:

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

mutex:必须是已经初始化过的互斥锁的地址

返回值:

成功:0

失败:错误码

3)解锁  -> pthread_mutex_unlock() -> man 3 pthread_mutex_unlock

功能:unlock a mutex

使用格式:

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

mutex:必须是已经初始化过的互斥锁的地址

返回值:

成功:0

失败:错误码

4)销毁互斥锁  -> pthread_mutex_destroy()  -> man 3 pthread_mutex_destroy

功能:

使用格式:

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

mutex:必须是已经初始化过的互斥锁的地址

返回值:

成功:0

失败:错误码

练习1:使用互斥锁完成作业2。

#include "head.h"

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

//Jack发

void *routine1(void *arg)

{

char *p = (char *)arg;

while(1)

{

//在访问共享内存前必须先上锁

pthread_mutex_lock(&m);

fgets(p,1024,stdin); //访问共享内存

//访问完共享内存后必须要解锁

pthread_mutex_unlock(&m);

if(strncmp(p,"quit",4) == 0)

{

break;

}

usleep(100000);

}

pthread_exit(NULL);

}

//Rose收

void *routine2(void *arg)

{

usleep(100000);

char *p = (char *)arg;

while(1)

{

//在访问共享内存前必须先上锁,由于睡觉了,后上锁

pthread_mutex_lock(&m);

printf("from Jack:%s",p);

pthread_mutex_unlock(&m);

if(strncmp(p,"quit",4) == 0)

{

break;

}

usleep(200000);

}

pthread_exit(NULL);

}

int main(int argc,char *argv[])

{

//1. 申请key与ID号

key_t key = ftok(".",10);

int shmid = shmget(key,1024,IPC_CREAT|0666);

//2. 映射内存空间

char *p = shmat(shmid,NULL,0);

//3. 产生Jack线程与Rose线程

pthread_t tid1,tid2;

pthread_create(&tid1,NULL,routine1,(void *)p);

pthread_create(&tid2,NULL,routine2,(void *)p);

//4. 接合线程

pthread_join(tid1,NULL);

pthread_join(tid2,NULL);

//5. 撤销映射,删除共享内存ID

shmdt(p);

shmctl(shmid,IPC_RMID,NULL);

return 0;

}

结论:只要工程涉及到共享资源(共享内存,链表,文件..),就必须在访问前上锁,访问后解锁。

二. 读写锁。

1. 互斥锁有什么缺陷?

互斥锁无论是访问资源,还是修改资源都必须要上锁,而且在上锁期间不能被别的线程再上锁。

访问资源(一起读这本书) -> 同时上读锁  ->  读锁其实是一把共享锁。

修改资源(修改书本的数据)  -> 不能同时上写锁  -> 写锁其实是一把互斥锁。

即有读锁,又有写锁,那么我们就称之为读写锁。

2. 关于读写锁的API函数接口?

1)初始化读写锁  -> pthread_rwlock_init()  -> man 3 pthread_rwlock_init

功能:initialize a read-write lock object

使用格式:

#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr);

rwlock: 读写锁的地址

attr: 属性,一般填NULL

返回值:

成功:0

失败:错误码

2)读锁上锁  -> pthread_rwlock_rdlock()  -> man 3 pthread_rwlock_rdlock

功能:lock a read-write lock object for reading

使用格式:

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

rwlock:读写锁的地址

返回值:

成功:0

失败:错误码

3)写锁上锁  -> pthread_rwlock_wrlock()  -> man 3 pthread_rwlock_wrlock

功能:lock a read-write lock object for writing

使用格式:

#include <pthread.h>

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

rwlock:读写锁的地址

返回值:

成功:0

失败:错误码

4)读写锁解锁  -> pthread_rwlock_unlock()  -> man 3 pthread_rwlock_unlock

功能:unlock a read-write lock object

使用格式:

#include <pthread.h>

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

rwlock:读写锁的地址

返回值:

成功:0

失败:错误码

5)销毁读写锁  -> pthread_rwlock_destroy()  -> man 3 pthread_rwlock_destroy

功能:destroy a read-write lock object

使用格式:

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

rwlock:读写锁的地址

返回值:

成功:0

失败:错误码

练习2:临界资源"int a",两个线程想打印a的值,两个线程想修改a的值。

验证:

1)读锁可以同时上,但是写锁不可以同时上。

2)读锁写锁能不能同时上?  -> 不可以,写锁等到读锁解开之后才能上写锁。

#include "head.h"

int a = 100;//临界资源

pthread_rwlock_t rwlock; //读写锁变量

//线程1任务:打印a的值,花了3秒

void *fun1(void *arg)

{

//访问临界资源前,需要上读锁

pthread_rwlock_rdlock(&rwlock);

printf("fun1 rdlock lock!\n");

printf("fun1 a = %d\n",a);

sleep(3);

//访问完临界资源,需要解锁

pthread_rwlock_unlock(&rwlock);

printf("fun1 rdlock unlock!\n");

pthread_exit(NULL);

}

//线程2任务:打印a的值,花了5秒

void *fun2(void *arg)

{

//访问临界资源前,需要上读锁

pthread_rwlock_rdlock(&rwlock);

printf("fun2 rdlock lock!\n");

printf("fun2 a = %d\n",a);

sleep(5);

//访问完临界资源,需要解锁

pthread_rwlock_unlock(&rwlock);

printf("fun2 rdlock unlock!\n");

pthread_exit(NULL);

}

//线程3任务:修改a的值,花了4S  a = 50

void *fun3(void *arg)

{

pthread_rwlock_wrlock(&rwlock);

printf("fun3 wrlock lock!\n");

a = 50;

sleep(4);

pthread_rwlock_unlock(&rwlock);

printf("fun3 wrlock unlock!\n");

pthread_exit(NULL);

}

//线程4任务:修改a的值,花了6S  a = 30

void *fun4(void *arg)

{

pthread_rwlock_wrlock(&rwlock);

printf("fun4 wrlock lock!\n");

a = 30;

sleep(6);

pthread_rwlock_unlock(&rwlock);

printf("fun4 wrlock unlock!\n");

pthread_exit(NULL);

}

void *routine(void *arg)

{

int i;

for(i=0;i<1000000;i++)

{

printf("%d\n",i);

sleep(1);

}

}

int main(int argc,char *argv[])

{

//0. 计算时间的流逝

pthread_t tid;

pthread_create(&tid,NULL,routine,NULL);

//1. 初始化读写锁

pthread_rwlock_init(&rwlock,NULL);

//2. 产生子线程

pthread_t tid1,tid2,tid3,tid4;

pthread_create(&tid1,NULL,fun1,NULL);

pthread_create(&tid2,NULL,fun2,NULL);

pthread_create(&tid3,NULL,fun3,NULL);

pthread_create(&tid4,NULL,fun4,NULL);

//3. 接合线程

pthread_join(tid1,NULL);

pthread_join(tid2,NULL);

pthread_join(tid3,NULL);

pthread_join(tid4,NULL);

//4. 销毁读写锁

pthread_rwlock_destroy(&rwlock);

return 0;

}

三. 条件变量。

1. 什么是条件变量?特点?

线程因为某个条件不成立/情况不允许情况下,进入一个变量中等待,这个可以存放这些线程的变量就叫做条件变量。

条件变量一定是与互斥锁一起使用。

2. 关于条件变量的函数接口?

0)定义条件变量 (数据类型:pthread_cond_t)

pthread_cond_t v;

1)初始化条件变量?  -> pthread_cond_init() -> man 3 pthread_cond_init

功能:initialize condition variables

使用格式:

动态初始化:

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr);

cond: 条件变量的地址

attr: 条件变量的属性,一般填NULL。

返回值:

成功:0

失败:错误码

静态初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2)如何进入条件变量中等待?  -> pthread_cond_wait() -> man 3 pthread_cond_wait

如果满足进入条件变量的条件,则会进去等待,并且会自动解锁。

功能:wait on a condition

使用格式:

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

cond:条件变量的地址

mutex:互斥锁的地址

返回值:

成功:0

失败:错误码

3)如何唤醒条件变量中等待的线程?

广播: 唤醒所有在条件变量中等待的线程。  -> pthread_cond_broadcast() -> man 3 pthread_cond_broadcast

单播: 随机唤醒条件变量中任意一个线程。  -> pthread_cond_signal()    -> man 3 pthread_cond_signal

功能:broadcast or signal a condition

使用格式:

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);

cond:条件变量的地址

返回值:

成功:0

失败:错误码

注意: 从条件变量中出来的线程,会自动上锁。

4)销毁条件变量  -> pthread_cond_destroy()  -> man 3 pthread_cond_destroy

功能:destroy condition variables

使用格式:

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

cond:条件变量的地址

返回值:

成功:0

失败:错误码

#include "head.h"

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁

pthread_cond_t v = PTHREAD_COND_INITIALIZER;//初始化条件变量

int sum = 400;

//任务: 就是去银行卡扣200块

void *routine(void *arg)

{

//1. 访问临界资源(银行卡)前,都需要上锁

pthread_mutex_lock(&m);

//2. 询问条件是否满足?

while(sum < 200) //当拿不到钱时

{

//就进入条件变量中等待

pthread_cond_wait(&v,&m); //自动解锁。

}

/* 从循环出来,一定是被唤醒并且余额>=200 */

printf("before money:%d\n",sum);

//拿钱

sum -= 200;

printf("after money:%d\n",sum);

//拿到钱后,要主动解锁。

pthread_mutex_unlock(&m);

//走人

pthread_exit(NULL);

}

void *routine1(void *arg)

{

int i;

for(i=0;i<1000000;i++)

{

printf("%d\n",i);

sleep(1);

}

}

int main(int argc,char *argv[])

{

//0. 计算时间的流逝

pthread_t tid_test;

pthread_create(&tid_test,NULL,routine1,NULL);

int i;

pthread_t tid[5];

//1. 产生5个子线程

for(i=0;i<5;i++)

{

pthread_create(&tid[i],NULL,routine,NULL);

}

sleep(5);  //2个能拿到钱,3个在条件变量中睡觉。

/* 主线程在访问临界资源前,也必须上锁 */

pthread_mutex_lock(&m);

sum += 400;

printf("main thread + 400!\n");

//拿完钱后,需要解锁

pthread_mutex_unlock(&m);

sleep(2);

//唤醒所有的线程起来拿钱

pthread_cond_broadcast(&v); //2个拿到钱,还有1个因为拿不到钱再回去睡觉。

printf("broarcast!\n");

sleep(3);

pthread_mutex_lock(&m);

sum += 200;

printf("main thread + 200!\n");

//拿完钱后,需要解锁

pthread_mutex_unlock(&m);

pthread_cond_signal(&v);

printf("signal!\n");

for(i=0;i<5;i++)

{

pthread_join(tid[i],NULL);

}

pthread_mutex_destroy(&m);

pthread_cond_destroy(&v);

return 0;

}

原文地址:https://www.cnblogs.com/zjlbk/p/11359558.html

时间: 2024-12-10 15:51:05

2019年8月15日星期四(系统编程)的相关文章

第一章 部署虚拟环境linux系统 2019年7月15日星期一 第二课

第一章 部署虚拟环境linux系统 2019年7月15日星期一  第二课 1.1 准备的工具 VmawareWorkSation 12.0   虚拟机 RadHatEnterpriseLinux[RHEL]7.0   红帽操作系统 1.2 安装配置VM虚拟机 略…… 1.3 安装配置VM虚拟机 VM配置要开启BIOS里的inte-TV  虚拟化服务 1.4 配置root用户密码 红帽RHCSA考前辅导视频 1.5 Rpm红帽软件包 (1)源代码安装弊端:1.难度高,安装困难. 2.需要自己解决依

蔡康永的说话之道——2019年12月15日

.bodyContainer { font-family: Arial, Helvetica, sans-serif; text-align: center; padding-left: 32px; padding-right: 32px; } .notebookFor { font-size: 18px; font-weight: 700; text-align: center; color: rgb(119, 119, 119); margin: 24px 0px 0px; padding:

2019年7月15日(C语言)

一.字符串函数  -> 追加字符串 strcat()  -> man 3 strcat 使用格式: #include <string.h> char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n); src:需要追加到另一个字符串默认的字符串的地址.  -> 拷贝之后会覆盖dest的\0. dest:被追加的字符串的空间,空间必须足够大. 返回

2019年9月19日星期四(STM32)

一.RTC(实时时钟) 1.背景 时间在计算机系统中是一个非常重要的参数,在很多应用场景下都需要使用时间.RTC就是一个专门记录时间的设备,本质上是一个1Hz定时器. 为了计时准确,计时系统掉电也要能够正常计时.为了到达这个目的,RTC需要独立的时钟源,独立的供电电源 stm32f407的RTC的时钟源是低速晶振(LSE---32.768KHz),使用纽扣电池单独供电 2.stm32f407的RTC RTC进行2次预分频,一次7位异步和15位的同步,最终得到1hz的时钟,为了降低功耗,异步预分频

2019年2月15日

2019年的第一周,寒假也在迷迷糊糊之中过去了.虽然很丢人 ,但也不得不承认,寒假在家期间没有学习.这几天主要是陪伴家人,和小侄女小侄子玩耍,拜年,走亲戚,剥花生等等. 因为舅舅回家的缘故,有了一次到鹤壁滑雪的机会,一个很美的度假村--桑园小镇.第一次玩,一次都没有从上面滑下来,倒是练习的时候一直摔,起起摔摔的好多次,衣服湿透了也没有真正意义上的滑过,每次都非常的被动.过程中最无助的是摔倒了却怎么也起不来,想脱掉鞋子也是怎么都脱不掉,只能等着衣服被雪浸透. 最开心的是小侄女生日那天吃了一个12寸

阮一峰网络日志 第43期 2019年02月15日

http://www.ruanyifeng.com/blog/2019/02/weekly-issue-43.html 声明:链接及文章内容为原博主阮一峰原创. 原文地址:https://www.cnblogs.com/sanen/p/10390610.html

2019年7月18日星期四(linux基础)

一.linux文件种类 1.如何查看linux下文件类型?如何决定文件类型? 使用"ls -l"查看文件的详细属性,其中第一个字母就是文件类型,是由创建方式来决定文件的类型.例如:使用mkdir命令来创建,就一定是目录文件. 2. 在linux下,一共有多少种文件类型 有7种.分别是: 文件类型        创建方式       字母 ====================================== 普通文件   ->    touch          - 目录文

2019年8月22日 星期四(总结重要的)

WebSocket 比如A,B,C3人同时在一个页面上浏览,A对B进行了评论,并且提到了C.传统的架构,B,C将无法感知到,只有B,C再次拉取内容时,才会得到此信息.显然B ,C错过了与A交互的最佳时机,而且得知信息的时间延后了很多. 传统的PULL模式,也可以通过定时轮询的方式来尝试拉取数据.但这种技术 1是并不是实时的,信息还是需要到了一定时间间隔才可以得到. 2,这种方式可能大部分请求是无效的,白白浪费了一次网络请求.还造成了web Server的负载大大增高. 真正的PUSH技术目前有3

2016年9月15日 星期四 --出埃及记 Exodus 17:11

As long as Moses held up his hands, the Israelites were winning, but whenever he lowered his hands, the Amalekites were winning. 摩西何时举手,以色列人就得胜:何时垂手,亚玛力人就得胜.