线程间同步
- 互斥锁
- 读写锁
- 信号量
进程间同步
- 信号量
- 互斥量
- 文件锁
互斥锁
pthread_mutext_t mutext;
特点:
- 阻塞,串行地访问共享数据
# include <stdio.h>
# include <pthread.h>
pthread_mutex_t mute;
int value = 0;
void *fun(void *arg){
//上锁,函数是阻塞的
pthread_mutex_lock(&mute);
printf("now is %d and old value is %d \n",*((int *)arg), value);
++value;
printf("now is %d and new value is %d \n",*((int *)arg), value);
//解锁
pthread_mutex_unlock(&mute);
}
int main(){
pthread_t threads[5];
int thread_id[5];
//创建锁,相当于new一个对象
pthread_mutex_init(&mute, NULL);
for(int i=0; i<5; ++i){
thread_id[i] = i;
pthread_create(&threads[i], NULL, fun, (void *)&thread_id[i]);
}
for(int i=0; i<5; ++i)
int rc = pthread_join(threads[i], NULL);
//释放互斥锁
pthread_mutex_destroy(&mute);
return 0;
}
读写锁
使用场景: 读的情况比写的情况多.
特性
读共享 - 并行处理
写独占 - 写的优先级高
场景:
线程A持有读锁, 然后线程B请求写锁,然后线程C请求读锁
result: B阻塞, C阻塞,然后 B获得锁, 最后C才获得锁
线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁
result 等到A释放锁以后, C是写锁, 优先级高 所以C先获得锁, 最后才是B
声明:
Int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, //初始化读写锁
const pthread_rwlockattr_t *restrict attr);
Int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //销毁读写锁
Int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //加读锁
Int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //尝试加读锁
Int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //加写锁
Int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); //尝试加写锁
Int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); //解锁
DEMO
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
int number = 0;
//创建读写锁
pthread_rwlock_t lock;
void* write_func(void* arg){
while (1)
{
//加写锁
pthread_rwlock_wrlock(&lock);
number++;
printf("write:thread %lu, number= %d \n", pthread_self(), number);
//解锁
pthread_rwlock_unlock(&lock);
usleep(500);
}
return NULL;
}
void* read_func(void* arg){
while (1)
{
//加读锁
pthread_rwlock_rdlock(&lock);
printf("read:thread %lu, number= %d \n", pthread_self(), number);
pthread_rwlock_unlock(&lock);
usleep(500);
}
return NULL;
}
int main(){
pthread_t p[8];
//初始化读写锁
pthread_rwlock_init(&lock, NULL);
//创建3个写线程
for (int i = 0; i < 3; i++)
{
pthread_create(&p[i], NULL, write_func, NULL);
}
//创建5个读线程
for (int i = 3; i < 8; i++)
{
pthread_create(&p[i], NULL, read_func, NULL);
}
//阻塞回收子线程资源的tcb
for (int i = 0; i < 8; i++)
{
pthread_join(p[i], NULL);
}
//释放读写锁
pthread_rwlock_destroy(&lock);
return 0;
}
条件变量
- 本质不是一把锁, 但是能够阻塞函数.
- 使用条件变量 + 互斥量
- 互斥量: 保护一块共享内存
- 条件变量: 引起阻塞
// 初始化一个条件变量
int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * attr);
// 销毁一个条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
// 阻塞等待一个条件变量
// 阻塞线程,将已上锁的mutex解锁,该函数解除阻塞会加互斥锁.
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
// 限时阻塞等待一个条件变量
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex, const struct timespec *restrict abstime);
//唤醒一个阻塞在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒全部阻塞在条件变量上的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
DEMO
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
//创建节点结构
typedef struct node{
int data;
struct node* next;
}Node;
//永远指向链表头部的指针
Node* head = NULL;
//线程同步 需要一个互斥锁
pthread_mutex_t mutex;
//阻塞线程 -- 条件变量类型的 变量
pthread_cond_t cond;
//生产者
void* producer(void* arg){
while(1){
//创建一个链表节点
Node* pnew = (Node*)malloc(sizeof(Node));
//节点的初始化
pnew->data = rand()%1000; //0-999
//使用互斥锁保护共享数据
pthread_mutex_lock(&mutex);
//指针域
pnew->next = head;
head = pnew;
printf("produce: %ul, %d\n", pthread_self(), pnew->data);
pthread_mutex_unlock(&mutex);
//通知阻塞的消费者线程, 解除阻塞
pthread_cond_signal(&cond);
sleep(rand() % 3);
}
return NULL;
}
void* costumer(void* arg){
while(1)
{
//使用互斥锁保护共享数据
pthread_mutex_lock(&mutex);
//消费前判断链表是否为空
if(head == NULL)
{
//线程阻塞
// 该函数会对互斥锁解锁.解除mutex阻塞之后,
// 等待对方完成后, 调用 pthread_cond_signal(&cond);
// 会在第一时间获得锁,对 mutex 互斥锁进行枷锁操作
pthread_cond_wait(&cond, &mutex);
//
}
//链表不为空,删掉一个头结点
Node* pdel = head;
head = head->next;
printf("---custumer: %ul, %d\n", pthread_self(), pdel->data);
free(pdel);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(){
pthread_t p1, p2;
//初始化
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
//创建生产者线程
pthread_create(&p1, NULL, producer, NULL);
//创建消费者线程
pthread_create(&p2, NULL, costumer, NULL);
//阻塞回收子线程
pthread_join(p1, NULL);
pthread_join(p2, NULL);
//回收
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
信号量
高级的互斥锁
// 成功返回0,失败返回-1;
// 参数sem:表示指向信号结构的指针。
// 参数pshared:不是0 的时候该信号量在进程间共享,否则只能在当前进程的所有线程间共享。
// 参数value:信号量的初始值。允许多少个线程or进程同时共享
int sem_init(sem_t * sem, int pshared, unsigned int value);//初始化信号量
int sem_destroy(sem_t *sem); //销毁信号量。
int sem_wait(sem_t *sem); //信号量减一操作,减少个资源数, 有线程申请资源
int sem_trywait(sem_t *sem); //sem==0 加锁失败,不阻塞,直接返回
int sem_timedwait(setm_t *sem); //限时尝试加锁
int sem_post(sem_t *sem); //信号量加一操作,增加资源数, 有线程释放资源
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
//创建节点结构
typedef struct node{
int data;
struct node* next;
}Node;
//永远指向链表头部的指针
Node* head = NULL;
//创建两个信号量
sem_t produce_sem;
sem_t custom_sem;
//生产者
void* producer(void* arg){
while(1){
//创建一个链表节点
Node* pnew = (Node*)malloc(sizeof(Node));
//节点的初始化
pnew->data = rand()%1000; //0-999
sem_wait(&produce_sem); //produce_sem--
//指针域
pnew->next = head;
head = pnew;
printf("produce: %lu, %d\n", pthread_self(), pnew->data);
sem_post(&custom_sem); //custom_sem++
sleep(rand() % 3);
}
return NULL;
}
void* costumer(void* arg){
while(1)
{
sem_wait(&custom_sem); //custom_sem--
//链表不为空,删掉一个头结点
Node* pdel = head;
head = head->next;
printf("---custumer: %lu, %d\n", pthread_self(), pdel->data);
free(pdel);
sem_post(&produce_sem);
sleep(rand()%5);
}
return NULL;
}
int main(){
pthread_t p1, p2;
//初始化
sem_init(&produce_sem, 0, 4); //初始化线程信号量
sem_init(&custom_sem, 0, 0); //初始化消费者线程信号量
//创建生产者线程
pthread_create(&p1, NULL, producer, NULL);
//创建消费者线程
pthread_create(&p2, NULL, costumer, NULL);
//阻塞回收子线程
pthread_join(p1, NULL);
pthread_join(p2, NULL);
//回收
sem_destroy(&produce_sem);
sem_destroy(&custom_sem);
return 0;
}