-
死锁4个条件:1)互斥条件 2)不可剥夺条件 3)请求和保持条件 4)循环等待条件
-
管程 :把锁和条件变量联合起来使用。锁实现互斥,条件变量实现同步。同步是协调多个线程运行速度
-
细度粒锁:减小一个锁所控制的共享数据量
-
递归锁:可以反复上锁,上锁多少次就要解锁多少次锁才能变为空闲。初始化pthread_mutex_t时将锁的类型设为PTHREAD_MUTEX_RECURSIVE即可设置递归锁
自旋锁
- test_and_set() : 测试并设置指令。硬件提供 讲某一个位置的值的读取和设置值能成为不可分割的操作
- 只适用于已知单个线程持有锁时间很短情况,缺点是效率低
struct lock {
int value;
}
void lock_acquire(struct lock *my_lock) { //获得锁
while (test_and_set(&my_lock->value))
;
return ;
}
void lock_unlock(struct lock *my_lock) { //解锁
my_lock->value = 0;
return ;
}
- 对换自旋锁. 硬件存储于内存中两个位置的值在一个不可分割的操作中对换
int test = 1;
do {
swap(&test, &lock);
} while (test);
锁实现线程间的同步
- mutex变量是非0即1,及可用资源数量
- 创建锁 及 销毁锁
#include <pthread.h>
//销毁pthread_mutex_init创建的mutex
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//对mutex初始化,attr为mutex的属性
//成功返回0,失败返回errno
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t * restrict attr);
//mutex变量是全局变量或static变量,可用以下宏初始化
//以下等价于pthread_mutex_init(mutex, NULL);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 加锁及解锁. 成功返回0 失败返回errno
#include <pthread.h>
//等待获得mutex锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//不等待获得mutex锁,若mutex被其他线程用则推出等待立即返回EBUSY
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
-
死锁
1)一个线程先后两次调用lock(),导致永远等待被自己占用的锁,陷入死锁
2)两个线程分别获得了锁后,分别调用对方的锁,导致双方永远挂起状态陷入死锁
条件变量实现线程间的同步
- 条件变量阻塞等待一个条件,或唤醒等待这个条件的线程
- 创建条件变量 及 销毁条件变量. 成功返回0,失败返回errno
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
//以下等价于pthread_cond_init(cond, NULL);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- 条件变量操作函数。成功返回0,失败返回errno
#include <pthread.h>
//abstime指定的时刻仍没有线程唤醒当前进程,返回ETIMEOUT
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
//阻塞等待条件变量发生
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
//唤醒条件变量下等待的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒条件变量下等待的线程
int pthread_cond_signal(pthread_cond_t *cond);
- 条件变量实现生产者与消费者
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t headlock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t hadGoods = PTHREAD_COND_INITIALIZER;
typedef struct Goods {
int data;
struct Goods *next;
} Goods;
Goods *head = NULL;
void *producer(void *arg) { //生成者
Goods *ng;
while (1) {
ng = (Goods *)malloc(sizeof(Goods));
ng->data = rand() % 100;
pthread_mutex_lock(&headlock);
//头插法
ng->next = head;
head = ng;
pthread_mutex_unlock(&headlock);
pthread_cond_signal(&hadGoods);
printf("producer %d\n", ng->data);
sleep(rand() % 3);
}
}
void *consumer(void *arg) { //消费者
Goods *k;
while (1) {
pthread_mutex_lock(&headlock);
if (!head) {
pthread_cond_wait(&hadGoods, &headlock);
}
k = head;
head = head->next;
pthread_mutex_unlock(&headlock);
printf("consumer %d\n", k->data);
free(k);
sleep(rand() % 3);
}
}
int main() {
srand(time(NULL));
pthread_t pid, cid;
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
信号量实现线程间的同步
-
信号量可用于同一进程间的同步,也可用于不同进程间的同步
semaphore可用资源数有多个,和mutex不同点在于可以大于1
#include <semaphore.h>
//初始化sem_t变量,value-可用资源数
//pshared=0表示信号量用于同一进程的线程间同步
int sem_init(sem_t *sem, int pshared, unsigned int value);
//获得资源,sem_t值减1,如果减1前值为0则挂起等待
int sem_wait(sem_t *sem);
//获得资源,sem_t值减1,如果减1前值为0则不会挂起等待
int sem_trywait(sem_t *sem);
//sem_t值加1,且唤醒挂起等待的线程
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem); //释放信号量资源
- 信号量实现生产者与消费者
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
int q[5];
sem_t nil_no, goods_no;
void *producer(void *arg) {
int i = 0;
while (1) {
sem_wait(&nil_no);
q[i] = rand() % 100 + 1;
printf("producer %d\n", q[i]);
sem_post(&goods_no);
i = (i + 1) % 5;
sleep(rand() % 3);
}
}
void *consumer(void *arg) {
int i = 0;
while (1) {
sem_wait(&goods_no);
printf("consumer %d\n", q[i]);
q[i] = 0;
sem_post(&nil_no);
i = (i + 1) % 5;
sleep(rand() % 3);
}
}
int main() {
srand(time(NULL));
sem_init(&nil_no, 0, 5);
sem_init(&goods_no, 0, 0);
//macOS
//sem_open("nil_no", O_CREAT|O_EXCL, S_IRWXU, 5);
//sem_open("goods_no", O_CREAT|O_EXCL, S_IRWXU, 0);
pthread_t pid, cid;
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}