文章目录
- 锁的原理以及锁的使用
- 互斥锁
- 锁的原理
- 死锁
锁的原理以及锁的使用
pthreah库中提供的锁的接口,以及类型
pthread_mutex_t //锁的类型
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER //这种定义在全局,不需要手动的释放pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) //初始化互斥锁。
pthread_mutex_destroy(pthread_mutex_t *mutex) //销毁互斥锁。pthread_mutex_lock(pthread_mutex_t *mutex)//获取互斥锁,如果锁已被其他线程占用,则阻塞等待。
pthread_mutex_trylock(pthread_mutex_t *mutex)//尝试获取互斥锁,如果锁已被占用,则立即返回。
pthread_mutex_unlock(pthread_mutex_t *mutex)//释放互斥锁
互斥锁
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>#define NUM_THREADS 5pthread_mutex_t lock; // 定义互斥锁
int shared_counter = 0; // 共享资源void* increment_counter(void* arg) {for (int i = 0; i < 100000; i++) {pthread_mutex_lock(&lock); // 获取锁shared_counter++; // 修改共享资源pthread_mutex_unlock(&lock); // 释放锁}return NULL;
}int main() {pthread_t threads[NUM_THREADS];// 初始化互斥锁pthread_mutex_init(&lock, NULL);// 创建多个线程for (int i = 0; i < NUM_THREADS; i++) {pthread_create(&threads[i], NULL, increment_counter, NULL);}// 等待所有线程完成for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}// 销毁互斥锁pthread_mutex_destroy(&lock);// 输出最终的计数器值printf("Final counter value: %d\n", shared_counter);return 0;
}
纯互斥的场景下,每个线程对于一个锁的竞争能力不同,可能会导致一个线程长久占据着锁,从而导致其他线程的饥饿问题。
解决方案:让所有线程在获取锁的过程按照一顶的顺序,释放锁的线程排到队列的尾部。(同步)
在临界区中,锁可以被切换吗?答案是可以。
但是我们被切换出去以后,我是持有锁被切换的,我不在的期间,照样也不能有其他线程进来。也就是说当前线程访问临界区的过程就是原子的。
锁的原理
原子性:通俗理解就是一条汇编语句就是原子的
一个锁变量定义在内存,它内部也有数据。在cpu内部有一个eax寄存器
上面的指令的意思是,第一步先将eax寄存器置为0,第二步则是将寄存器内部的数据与内存中锁变量的值做交换,内存中锁变量的值通常为1。当一个线程发现寄存器中的值为0时,说明申请锁失败,需要等待。
交换的本质是:把内存中的数据与,交换到cpu的寄存器中,内存中的数据是所有的线程共享的,寄存器与内存交换数据后,这些内容将会保存到线程的上下文当中。线程的上下文是线程私有的。
也就是说内存中的1只有一个,而每个线程都是存的0,申请锁的过程就是持有这个1,这个1将会随着线程的切换保存在线程的上下文中。
交换到自己的线程上下文中就说明这个线程持有了锁。
死锁
死锁是指一组进程中各个进程均占有不会释放的资源,但因为互相申请被其他进程所占用不会释放资源而处于一种永久等待的状态。
死锁需要同时满足这4个条件:
互斥条件:一个资源每次只能被一个执行流申请使用
请求与保持条件:一个执行流因为请求资源而阻塞时,对已经获得的资源保持不放
不剥夺条件:一个执行流对已经获得的资源,在没有使用完之前,不能强行剥夺
循环等待条件:若干个执行流之间形成一种头尾相接的循环等待资源的关系。
解决方法:破环四个必要条件