Home Article Practice 08_线程的同步与互斥

08_线程的同步与互斥

2024-05-06 08:41  views:167  source:拼搏百天我要上蓝翔    

知识点1【同步互斥的概念】(了解)互斥:同一时间,只能一个任务(进程或线程)执行,谁先运行不确定。同步:同一时
间,只能一个任务(进程或线程)执行,有顺序的运行。同步 是特殊的 互斥。知识点2【互斥锁】用于线程的互斥。互斥
锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即加锁(lock )和解锁( unlock
)互斥锁的操作流程如下:1)在访问共享资源临界区域前,对互斥锁进行加锁。2)在访问完成后释放互斥锁上的锁。
(解锁)3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。互斥锁的数据类型是
: pthread_mutex_t1、Pthread_mutex_init 函数1 #include <pth
read。h>2 int pthread_mutex_init(pthread_mutex_t *mutex,
3 const pthread_mutexattr_t *attr);功能:初始化一个互斥锁。参数:mutex
:互斥锁地址。类型是 pthread_mutex_t 。attr:设置互斥量的属性,通常可采用默认属性,即可将
attr 设为 NULL。可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,
比如:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;这种
方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始
化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。返回值:成功:0,成功
申请的锁默认是打开的。失败:非 0 错误码2、销毁互斥锁1 #include <pthread。h>2 int
pthread_mutex_destroy(pthread_mutex_t *mutex);功能:销毁指定的
一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。参数:mutex:互斥锁地址。返回值:成功
:0失败:非 0 错误码3、申请上锁1 #include <pthread。h>2 int pthread_m
utex_lock(pthread_mutex_t *mutex);功能:对互斥锁上锁,若互斥锁已经上锁,则调
用者阻塞,直到互斥锁解锁后再上锁。参数:mutex:互斥锁地址。返回值:成功:0失败:非 0 错误码1 int
pthread_mutex_trylock(pthread_mutex_t *mutex);调用该函数时,若
互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即 EBUSY。4、解锁1 #includ
e <pthread。h>2 int pthread_mutex_unlock(pthread_mutex_t
*mutex);功能:对指定的互斥锁解锁。参数:mutex:互斥锁地址。返回值:成功:0失败:非0错误码案例
1:没有互斥锁 多任务的运行情况1 #include <stdio。h>2 #include <pthread
。h>3 #include <unistd。h>4 void *deal_fun01(void *arg)5
{6 char *str = (char *)arg;7 int i = 0;8 while (str[i]
!= '\0')9 {10 printf("%c", str[i++]);11 fflush(stdout);
//强制刷新12 sleep(1);13 }1415 return NULL;16 }1718 void *
deal_fun02(void *arg)19 {20 char *str = (char *)arg;21
int i = 0;22 while (str[i] != '\0')23 {24 printf("%c",
str[i++]);25 fflush(stdout); //强制刷新26 sleep(1);27 }2829
return NULL;30 }31 int main(int argc, char const *argv
[])32 {33 //创建两个线程3435 pthread_t tid1, tid2;36 pthread_
create(&tid1, NULL, deal_fun01, "hello");37 pthread_cre
ate(&tid2, NULL, deal_fun02, "world");3839 pthread_join
(tid1, NULL);40 pthread_join(tid2, NULL);4142 return 0;
43 }案例2:有互斥锁 多任务的运行情况1 #include <stdio。h>2 #include <pt
hread。h>3 #include <unistd。h>4 //定义一把锁5 pthread_mutex_t
mutex;67 void *deal_fun01(void *arg)8 {9 char *str = (
char *)arg;10 int i = 0;1112 //上锁13 pthread_mutex_lock(
&mutex);1415 while (str[i] != '\0')16 {17 printf("%c",
str[i++]);18 fflush(stdout); //强制刷新19 sleep(1);20 }2122
//解锁23 pthread_mutex_unlock(&mutex);2425 return NULL;2
6 }2728 void *deal_fun02(void *arg)29 {30 char *str = (
char *)arg;31 int i = 0;3233 //上锁34 pthread_mutex_lock(
&mutex);3536 while (str[i] != '\0')37 {38 printf("%c",
str[i++]);39 fflush(stdout); //强制刷新40 sleep(1);41 }4243
//解锁44 pthread_mutex_unlock(&mutex);4546 return NULL;4
7 }48 int main(int argc, char const *argv[])49 {50 //初始
化一把锁51 pthread_mutex_init(&mutex, NULL);5253 //创建两个线程54
pthread_t tid1, tid2;55 pthread_create(&tid1, NULL, de
al_fun01, "hello");56 pthread_create(&tid2, NULL, deal_
fun02, "world");5758 pthread_join(tid1, NULL);59 pthrea
d_join(tid2, NULL);6061 //销毁锁62 pthread_mutex_destroy(&
mutex);6364 return 0;65 }66 #include <stdio。h>67 #inclu
de <pthread。h>68 #include <unistd。h>69 //定义一把锁70 pthrea
d_mutex_t mutex;7172 void *deal_fun01(void *arg)73 {74
char *str = (char *)arg;75 int i = 0;7677 //上锁78 pthrea
d_mutex_lock(&mutex);7980 while (str[i] != '\0')81 {82
printf("%c", str[i++]);83 fflush(stdout); //强制刷新84 slee
p(1);85 }8687 //解锁88 pthread_mutex_unlock(&mutex);8990
return NULL;91 }9293 void *deal_fun02(void *arg)94 {95
char *str = (char *)arg;96 int i = 0;9798 //上锁99 pthrea
d_mutex_lock(&mutex);100101 while (str[i] != '\0')102 {
103 printf("%c", str[i++]);104 fflush(stdout); //强制刷新10
5 sleep(1);106 }107108 //解锁109 pthread_mutex_unlock(&mu
tex);110111 return NULL;112 }113 int main(int argc, cha
r const *argv[])114 {115 //初始化一把锁116 pthread_mutex_init
(&mutex, NULL);117118 //创建两个线程119 pthread_t tid1, tid2;
120 pthread_create(&tid1, NULL, deal_fun01, "hello");12
1 pthread_create(&tid2, NULL, deal_fun02, "world");1221
23 pthread_join(tid1, NULL);124 pthread_join(tid2, NULL
);125126 return 0;127 }总结:如果是互斥 不管有多少个任务,只需要一把锁,所有的任务上锁
访问资源 解锁。知识点3【死锁】知识点4【读写锁】POSIX 定义的读写锁的数据类型是: pthread_r
wlock_t1、初始化读写锁1 #include <pthread。h>2 int pthread_rwlo
ck_init(pthread_rwlock_t *rwlock,3 const pthread_rwlock
attr_t *attr);功能:用来初始化 rwlock 所指向的读写锁。参数:rwlock:指向要初始化的
读写锁指针。attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则使用
指定的 attr 初始化读写锁。可以使用宏 PTHREAD_RWLOCK_INITIALIZER 静态初始化读
写锁,比如:pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITI
ALIZER;这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_rwlock_init
() 来完成动态初始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。返
回值:成功:0,读写锁的状态将成为已初始化和已解锁。失败:非 0 错误码。2、释放读写锁1 #include
<pthread。h>2 int pthread_rwlock_destroy(pthread_rwlock_
t *rwlock);功能:用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是由pthread_rw
lock_init() 自动申请的资源) 。参数:rwlock:读写锁指针。返回值:成功:0失败:非 0 错误
码3、申请读锁1 #include <pthread。h>2 int pthread_rwlock_rdloc
k(pthread_rwlock_t *rwlock);功能:以阻塞方式在读写锁上获取读锁(读锁定)。如果没有
写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。如果调用线程未获取读锁,则它将阻塞直到它获取了该
锁。一个线程可以在一个读写锁上多次执行读锁定。线程可以成功调用 pthread_rwlock_rdlock()
函数 n 次,但是之后该线程必须调用pthread_rwlock_unlock() 函数 n 次才能解除锁定
。参数:rwlock:读写锁指针。返回值:成功:0失败:非 0 错误码1 int pthread_rwlock
_tryrdlock(pthread_rwlock_t *rwlock);用于尝试以非阻塞的方式来在读写锁上获
取读锁。如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回4、申请写锁1 #include <p
thread。h>2 int pthread_rwlock_wrlock(pthread_rwlock_t *
rwlock);功能:在读写锁上获取写锁(写锁定)。如果没有写者持有该锁,并且没有写者读者持有该锁,则调用线程
会获取写锁。如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。参数:rwlock:读写锁指针。返回值:成功:
0失败:非 0 错误码1 int pthread_rwlock_trywrlock(pthread_rwloc
k_t *rwlock);用于尝试以非阻塞的方式来在读写锁上获取写锁。如果有任何的读者或写者持有该锁,则立即失
败返回5、释放读写锁1 #include <pthread。h>2 int pthread_rwlock_un
lock(pthread_rwlock_t *rwlock);功能:无论是读锁或写锁,都可以通过此函数解锁。参
数:rwlock:读写锁指针。返回值:成功:0失败:非 0 错误码案例1:两个任务读 一个任务写1 #incl
ude <stdio。h>2 #include <pthread。h>3 #include <unistd。h
>4 //定义一把锁5 pthread_rwlock_t rwlock;67 void *read_data0
1(void *arg)8 {9 int *p = (int *)arg;10 while (1)11 {12
//申请上读锁13 pthread_rwlock_rdlock(&rwlock);14 printf("任务
A:num=%d\n", *p);15 //解读写锁16 pthread_rwlock_unlock(&rwl
ock);17 sleep(1);18 }1920 return NULL;21 }22 void *read
_data02(void *arg)23 {24 int *p = (int *)arg;25 while (
1)26 {27 //申请上读锁28 pthread_rwlock_rdlock(&rwlock);2930
printf("任务B:num=%d\n", *p);31 //解读写锁32 pthread_rwlock_u
nlock(&rwlock);33 sleep(1);34 }35 return NULL;36 }37 vo
id *write_data(void *arg)38 {39 int *p = (int *)arg;40
while (1)41 {42 //申请写锁43 pthread_rwlock_wrlock(&rwlock)
;44 (*p)++;45 //解读写锁46 pthread_rwlock_unlock(&rwlock);4
7 printf("任务C:写入num=%d\n", *p);48 sleep(2);49 }50 retur
n NULL;51 }5253 int main(int argc, char const *argv[])5
4 {55 //定义一个公共资源56 int num = 0;5758 //初始化一把锁59 pthread_
rwlock_init(&rwlock, NULL);6061 //创建两个线程62 pthread_t ti
d1, tid2, tid3;63 pthread_create(&tid1, NULL, read_data
01, (void *)&num); //读64 pthread_create(&tid2, NULL, re
ad_data02, (void *)&num); //读65 pthread_create(&tid3, N
ULL, write_data, (void *)&num); //写6667 pthread_join(ti
d1, NULL);68 pthread_join(tid2, NULL);69 pthread_join(t
id3, NULL);7071 //销毁锁72 pthread_rwlock_destroy(&rwlock)
;73 return 0;74 }知识点5【条件变量】(重要)条件变量是用来等待而不是用来上锁的,条件变量本身
不是锁。条件变量和互斥锁同时使用。条件变量的两个动作: 条件不满, 阻塞线程 当条件满足, 通知阻塞的线程开始
工作。条件变量的类型: pthread_cond_t。1、条件变量初始化1 #include <pthread
。h>2 int pthread_cond_init(pthread_cond_t *cond,3 const
pthread_condattr_t *attr);功能:初始化一个条件变量参数:cond:指向要初始化的条
件变量指针。attr:条件变量属性,通常为默认值,传NULL即可也可以使用静态初始化的方法,初始化条件变量:p
thread_cond_t cond = PTHREAD_COND_INITIALIZER;返回值:成功:0失
败:非0错误号2、释放条件变量1 #include <pthread。h>2 int pthread_cond
_destroy(pthread_cond_t *cond);功能:销毁一个条件变量参数:cond:指向要初始
化的条件变量指针返回值:成功:0失败:非0错误号3、等待条件1 #include <pthread。h>2 i
nt pthread_cond_wait(pthread_cond_t *cond,3 pthread_mut
ex_t *mutex);功能:阻塞等待一个条件变量先解锁、等待条件满足、重新上锁(3步为原子操作)解阻塞参数
:cond:指向要初始化的条件变量指针mutex:互斥锁返回值:成功:0失败:非0错误号1 int pthre
ad_cond_timedwait(pthread_cond_t *cond,2 pthread_mutex_
t *mutex,3 const struct *abstime);功能:限时等待一个条件变量参数:cond:
指向要初始化的条件变量指针mutex:互斥锁abstime:绝对时间返回值:成功:0失败:非0错误号4、唤醒等
待在条件变量上的线程1 #include <pthread。h>2 int pthread_cond_sign
al(pthread_cond_t *cond);功能:唤醒至少一个阻塞在条件变量上的线程参数cond:指向要
初始化的条件变量指返回值成功:0失败:非0错误号1 int pthread_cond_broadcast(pt
hread_cond_t *cond);功能:唤醒全部阻塞在条件变量上的线程参数:cond:指向要初始化的条件
变量指针返回值:成功:0失败:非0错误号5、案例:生产者和消费者1 #include <stdio。h>2 #
include <pthread。h>3 #include <unistd。h>4 #include <tim
e。h>5 #include <stdlib。h>6 //定义互斥锁7 pthread_mutex_t mut
ex;8 //定义条件变量9 pthread_cond_t cond;10 //定义一个仓库 默认有3个产品1
1 int num = 3;12 void *consumption_function(void *arg)
//消费13 {14 while (1)15 {16 //申请上锁17 pthread_mutex_lock(
&mutex);1819 //判断仓库是否为空 如果为空 等待条件变量满足20 if (num == 0) /
/仓库为空21 {22 printf("%s发现仓库为空 等待生产\n", (char *)arg);23 p
thread_cond_wait(&cond, &mutex);24 }2526 //进入仓库购买产品27 i
f (num > 0)28 {29 ‐‐num;30 printf("%s购买了一个产品 仓库剩余%d个\n"
, (char *)arg, num);31 //使用产品32 printf("%s正在使用产品\n", (c
har *)arg);33 }3435 //解锁36 pthread_mutex_unlock(&mutex)
;3738 sleep(rand() % 5);39 }40 return NULL;41 }4243 voi
d *production_function(void *arg) //生产44 {45 while (1)4
6 {47 //生产一个产品48 sleep(rand() % 5);4950 //上锁 进入仓库51 pth
read_mutex_lock(&mutex);5253 //将产品放入仓库54 num++;55 print
f("%s 放入一个产品, 仓库剩余%d个\n", (char *)arg, num);5657 //通知 条
件变量阻塞的线程58 pthread_cond_broadcast(&cond);5960 //解锁61 pt
hread_mutex_unlock(&mutex);62 }63 return NULL;64 }6566
int main(int argc, char const *argv[])67 {68 //设置随机数种子6
9 srand(time(NULL));70 //初始化锁71 pthread_mutex_init(&mut
ex, NULL);72 //初始化条件变量73 pthread_cond_init(&cond, NULL)
;7475 pthread_t tid1, tid2, tid3;76 pthread_create(&tid
1, NULL, consumption_function, "消费者A");77 pthread_creat
e(&tid2, NULL, consumption_function, "消费者B");78 pthread
_create(&tid3, NULL, production_function, "生产者A");7980
//等线程结束81 pthread_join(tid1, NULL);82 pthread_join(tid2
, NULL);83 pthread_join(tid3, NULL);8485 //销毁锁86 pthrea
d_mutex_destroy(&mutex);87 //销毁条件变量88 pthread_cond_dest
roy(&cond);89 return 0;90 }知识点6【信号量】1、信号量信号量广泛用于进程或线程间的
同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当信号量值大于 0 时,则可以访
问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。号量数据类
型为:sem_t信号量用于互斥:不管多少个任务互斥 只需要一个信号量。先P 任务 在 V信号量用于同步:有多少
个任务 就需要多少个信号量。最先执行的任务对应的信号量为1,其他信号量全部为0。每任务先P自己 任务 V下一个
任务的信号量2、信号量的API1、初始化信号量1 #include <semaphore。h>2 int se
m_init(sem_t *sem, int pshared, unsigned int value)功能:创
建一个信号量并初始化它的值。一个无名信号量在被使用前必须先初始化。参数:sem:信号量的地址pshared:等
于 0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。value:信号量的初始值返回值:成功:0失
败: - 12、信号量减一 P操作1 int sem_wait(sem_t *sem);功能: 将信号量减一,
如果信号量的值为0 则阻塞,大于0可以减一参数:信号量的地址返回值:成功返回0 失败返回-1尝试对信号量减一1
int sem_trywait(sem_t *sem);功能: 尝试将信号量减一,如果信号量的值为0 不阻塞
,立即返回 ,大于0可以减一参数:信号量的地址返回值:成功返回0 失败返回-13、信号量加一 V操作1 int
sem_post(sem_t *sem);功能:将信号量加一参数:信号量的地址返回值:成功返回0 失败返回-
14、销毁信号量1 int sem_destroy(sem_t *sem);功能: 销毁信号量参数: 信号量的
地址返回值:成功返回0 失败返回-1知识点7【信号量用于线程的互斥】1 #include <stdio。h>2
#include <pthread。h>3 #include <semaphore。h>4 #include
<unistd。h>5 //定义一个信号量(用于互斥)6 sem_t sem;78 void my_prin
tf(char *str)9 {10 int i = 0;11 while (str[i] != '\0')1
2 {13 printf("%c", str[i++]);14 fflush(stdout);15 sleep
(1);16 }17 return;18 }19 void *task_fun01(void *arg)20
{21 //P 操作22 sem_wait(&sem);2324 my_printf((char *)arg)
;2526 //V 操作27 sem_post(&sem);2829 return NULL;30 }31 v
oid *task_fun02(void *arg)32 {33 //P 操作34 sem_wait(&sem
);3536 my_printf((char *)arg);3738 //V 操作39 sem_post(&s
em);4041 return NULL;42 }43 void *task_fun03(void *arg)
44 {45 //P 操作46 sem_wait(&sem);4748 my_printf((char *)a
rg);4950 //V 操作51 sem_post(&sem);5253 return NULL;54 }5
5 int main(int argc, char const *argv[])56 {57 //信号量初始化
为1 第二个参数0表示用于线程58 sem_init(&sem, 0, 1);5960 pthread_t t
id1, tid2, tid3;6162 pthread_create(&tid1, NULL, task_f
un01, "hello");63 pthread_create(&tid2, NULL, task_fun0
2, "world");64 pthread_create(&tid3, NULL, task_fun03,
"beijing");6566 pthread_join(tid1, NULL);67 pthread_joi
n(tid2, NULL);68 pthread_join(tid3, NULL);6970 //销毁信号量7
1 sem_destroy(&sem);72 return 0;73 }知识点8【信号量用于线程的同步】1 #
include <stdio。h>2 #include <pthread。h>3 #include <sema
phore。h>4 #include <unistd。h>5 //定义三个信号量(用于同步)6 sem_t s
em1;7 sem_t sem2;8 sem_t sem3;910 void my_printf(char *
str)11 {12 int i = 0;13 while (str[i] != '\0')14 {15 pr
intf("%c", str[i++]);16 fflush(stdout);17 sleep(1);18 }
19 return;20 }21 void *task_fun01(void *arg)22 {23 //P
操作24 sem_wait(&sem1);2526 my_printf((char *)arg);2728 /
/V 操作29 sem_post(&sem2);3031 return NULL;32 }33 void *t
ask_fun02(void *arg)34 {35 //P 操作36 sem_wait(&sem2);373
8 my_printf((char *)arg);3940 //V 操作41 sem_post(&sem3);
4243 return NULL;44 }45 void *task_fun03(void *arg)46 {
47 //P 操作48 sem_wait(&sem3);4950 my_printf((char *)arg)
;5152 //V 操作53 sem_post(&sem1);5455 return NULL;56 }57
int main(int argc, char const *argv[])58 {59 //信号量初始化为1
第二个参数0表示用于线程60 sem_init(&sem1, 0, 1);61 sem_init(&sem2
, 0, 0);62 sem_init(&sem3, 0, 0);6364 pthread_t tid1, t
id2, tid3;6566 pthread_create(&tid1, NULL, task_fun01,
"hello");67 pthread_create(&tid2, NULL, task_fun02, "wo
rld");68 pthread_create(&tid3, NULL, task_fun03, "beiji
ng");6970 pthread_join(tid1, NULL);71 pthread_join(tid2
, NULL);72 pthread_join(tid3, NULL);7374 //销毁信号量75 sem_
destroy(&sem1);76 sem_destroy(&sem2);77 sem_destroy(&se
m3);7879 return 0;80 }知识点9【无名信号量 用于 血缘关系的进程间互斥】使用mmap完成
无名信号量的定义;1 #include <stdio。h>2 #include <semaphore。h>3
#include <unistd。h>4 #include <sys/mman。h>5 //定义三个信号量(用
于同步)6 sem_t sem1;7 sem_t sem2;8 sem_t sem3;910 void my_
printf(char *str)11 {12 int i = 0;13 while (str[i] != '
\0')14 {15 printf("%c", str[i++]);16 fflush(stdout);17
sleep(1);18 }19 return;20 }2122 int main(int argc, char
const *argv[])23 {24 //定义一个无名信号量25 //MAP_ANONYMOUS匿名映射
‐1不需要文件描述符26 sem_t *sem = mmap(NULL, sizeof(sem_t), PR
OT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, ‐1, 0
);2728 //无名信号量的初始化 第一个1表示进程 第二个1表示初始化值129 sem_init(sem,
1, 1);3031 pid_t pid = fork();32 if (pid == 0) //子进程33
{34 //P操作35 sem_wait(sem);3637 my_printf("hello");3839
//V操作40 sem_post(sem);41 }42 else if (pid > 0) //父进程43
{44 //P操作45 sem_wait(sem);4647 my_printf("world");4849
//V操作50 sem_post(sem);51 }5253 //销毁信号量54 sem_destroy(s
em);55 return 0;56 }知识点10【无名信号量 用于 血缘关系的进程间同步】1 #includ
e <stdio。h>2 #include <semaphore。h>3 #include <unistd。h
>4 #include <sys/mman。h>5 //定义三个信号量(用于同步)6 sem_t sem1;7
sem_t sem2;8 sem_t sem3;910 void my_printf(char *str)1
1 {12 int i = 0;13 while (str[i] != '\0')14 {15 printf(
"%c", str[i++]);16 fflush(stdout);17 sleep(1);18 }19 re
turn;20 }2122 int main(int argc, char const *argv[])23
{24 //定义一个无名信号量25 //MAP_ANONYMOUS匿名映射 ‐1不需要文件描述符26 sem_
t *sem1 = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WR
ITE, MAP_SHARED | MAP_ANON



Disclaimer: The above articles are added by users themselves and are only for typing and communication purposes. They do not represent the views of this website, and this website does not assume any legal responsibility. This statement is hereby made! If there is any infringement of your rights, please contact us promptly to delete it.

字符:    改为:
去打字就可以设置个性皮肤啦!(O ^ ~ ^ O)