一、线程互斥方式。 --- 互斥锁 1、什么是互斥锁?特点怎么样? 互斥锁是专门用于处理线程之间互斥的一种方式,它有两种:上锁状态/解锁状态。 如果互斥锁处于上锁状态,那么再上锁就会阻塞,直到这把锁解开之后,才能上锁。 如果互斥锁处于解锁状态,那么再解锁依然可以的,不会阻塞。
2、 互斥锁函数接口? 1)定义互斥锁变量 -> 数据类型: pthread_mutex_t pthread_mutex_t m;
2)初始化互斥锁。 -> pthread_mutex_init() -> man 3 pthread_mutex_init 动态初始化: 头文件: #include <pthread.h>
原型: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
参数: mutex:互斥锁变量的地址。 mutexattr:普通属性,填NULL。
返回值: 成功:0 失败:非0
静态初始化: pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; 只要把该宏定义赋值给互斥锁变量,就等价于初始化了这把互斥锁。
======================== 也就是说: pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
等价于 pthread_mutex_t m; pthread_mutex_init(&m,NULL);
3)上锁。 -> pthread_mutex_lock() -> man 3 pthread_mutex_lock 头文件: #include <pthread.h>
原型: int pthread_mutex_lock(pthread_mutex_t *mutex);
参数: mutex:互斥锁变量的地址。
返回值: 成功:0 失败:非0
4)解锁。 -> pthread_mutex_unlock() -> man 3 pthread_mutex_unlock 头文件: #include <pthread.h>
原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数: mutex:互斥锁变量的地址。
返回值: 成功:0 失败:非0
5)销毁互斥锁。 -> pthread_mutex_destroy() -> man 3 pthread_mutex_destroy 头文件: #include <pthread.h>
原型: int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数: mutex:互斥锁变量的地址。
返回值: 成功:0 失败:非0
一般地: 多个线程任务都一样的 -> 互斥锁 多个线程任务不一样的 -> 无名信号量
练习1: 使用互斥锁完成5个线程打印helloworld。 -> 多个线程任务都一样的 练习2: 使用互斥锁完成<练习.docx> -> 多个线程任务不一样的
二、线程互斥方式。 -- 读写锁 1、互斥锁有什么缺陷? 互斥锁无论是读取共享资源,还是修改共享资源,都要上锁,而且在上锁期间,不能被别的线程上锁。
访问资源(一起读一本书) -> 同时上读锁 -> 读锁就是一把共享锁。 修改资源(一起做一份试卷) -> 不能同时上写锁 -> 写锁就是一把互斥锁。
这把既有读锁,又有写锁的锁,就称之为读写锁。
2、读写锁函数接口? 1)定义一个读写锁变量。 数据类型: pthread_rwlock_t pthread_rwlock_t rwlock;
2)初始化读写锁? -> pthread_rwlock_init() -> man 3 pthread_rwlock_init 动态初始化: 头文件: #include <pthread.h>
原型: int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
参数: rwlock: 读写锁的地址。 attr: 普通属性,填NULL。
返回值: 成功:0 失败:非0
静态初始化: pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
3)读锁上锁。 -> pthread_rwlock_rdlock() -> man 3 pthread_rwlock_rdlock 头文件: #include <pthread.h>
原型: int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数: rwlock: 读写锁的地址。
返回值: 成功:0 失败:非0
4)写锁上锁。 -> pthread_rwlock_wrlock() -> man 3 pthread_rwlock_wrlock 头文件: #include <pthread.h> 原型: int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数: rwlock: 读写锁的地址。
返回值: 成功:0 失败:非0
5)解锁。 -> pthread_rwlock_unlock() -> man 3 pthread_rwlock_unlock 头文件: #include <pthread.h>
原型: int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数: rwlock: 读写锁的地址。
返回值: 成功:0 失败:非0
6)销毁读写锁。 -> pthread_rwlock_destroy() -> man 3 pthread_rwlock_destroy 头文件: #include <pthread.h> 原型: int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数: rwlock: 读写锁的地址。
返回值: 成功:0 失败:非0
练习3: 现有一个临界资源: "int a" -> 全局变量 现在有4个线程,有两个线程想打印a的值 -> 读 thread1: 3S thread2:5S -> 看时间 -> 如果读完需要8S,则读锁不能同时上
有两个线程想修改a的值,一个想改成30,一个想改成50 -> 写 thread3: 4S thread4: 6S 再开一条线程,用于倒数时间就行。
验证:1)读锁可以同时上,写锁不可以同时上。 2)读锁与写锁能不能同时上? -> 不可以,读锁要等到写锁解开了之后才能上锁。 #include "head.h"
int a = 100; //临界资源 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void *func_time(void *arg) { int i; for(i=0;i<1000;i++) { printf("i = %d\n",i); sleep(1); } }
//线程1: 打印a的值。 -> 3S (读操作) void *func1(void *arg) { //1. 访问资源前,先上读锁。 pthread_rwlock_rdlock(&rwlock); printf("thread 1 rdlock lock!\n"); //2. 打印a的值。 printf("a = %d\n",a); //3. 持续3S sleep(3); //4. 访问完资源了,需要解锁。 pthread_rwlock_unlock(&rwlock); printf("thread 1 rdlock unlock!\n"); //5. 线程退出 pthread_exit(NULL); }
//线程2: 打印a的值。 -> 5S (读操作) void *func2(void *arg) { //1. 访问资源前,先上读锁。 pthread_rwlock_rdlock(&rwlock); printf("thread 2 rdlock lock!\n"); //2. 打印a的值。 printf("a = %d\n",a); //3. 持续5S sleep(5); //4. 访问完资源了,需要解锁。 pthread_rwlock_unlock(&rwlock); printf("thread 2 rdlock unlock!\n"); pthread_exit(NULL); }
//线程3:修改a的值为50。 -> 4S void *func3(void *arg) { //1. 修改资源之前,需要上写锁。 pthread_rwlock_wrlock(&rwlock); printf("thread 3 wrlock lock!\n"); //2. 修改临界资源。 a = 50; //3. 持续一段时间。 sleep(4); //4. 修改资源后,需要解锁。 pthread_rwlock_unlock(&rwlock); printf("thread 3 wrlock unlock!\n"); pthread_exit(NULL); }
//线程4:修改a的值为30。 -> 6S void *func4(void *arg) { //1. 修改资源之前,需要上写锁。 pthread_rwlock_wrlock(&rwlock); printf("thread 4 wrlock lock!\n"); //2. 修改临界资源。 a = 100; //3. 持续一段时间。 sleep(6); //4. 修改资源后,需要解锁。 pthread_rwlock_unlock(&rwlock); printf("thread 4 wrlock unlock!\n"); pthread_exit(NULL); }
int main(int argc,char *argv[]) { //0. 创建一个用于倒数时间线程 pthread_t tid_time; pthread_create(&tid_time,NULL,func_time,NULL); //1、 创建2个子线程,用于打印临界资源的值。 pthread_t tid1,tid2,tid3,tid4; pthread_create(&tid1,NULL,func1,NULL); pthread_create(&tid2,NULL,func2,NULL); pthread_create(&tid3,NULL,func3,NULL); pthread_create(&tid4,NULL,func4,NULL); //2. 接合线程 pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_join(tid4,NULL); //3. 销毁读写锁 pthread_rwlock_destroy(&rwlock); return 0; }