Linux系统编程学习笔记_008_线程同步

目录

概念

互斥锁

死锁

读写锁

条件变量

pthread_cond_wait函数

生产者消费者模型

信号量


概念

线程同步,指一个线程发出某一功能调用时,在没有得到结果前,该调用不返回,同时其它线程为保证数据一致性,不能调用该功能。

协同步调,对公共区域数据按序访问。防止数据混乱,产生与时间有关的错误。

数据混乱的原因:

1. 资源共享(独享资源则不会)

2. 调度随机(意味着数据访问会出现竞争)

3. 线程间缺乏必要同步机制

互斥锁

Linux提供mutex(互斥锁)

锁本身不具备强制性,所有线程应该在访问公共数据前拿锁

主要应用函数:

pthread_mutex_init 函数 初始化 锁初值1

pthread_mutex_destory 函数 销毁锁

pthread_mutex_lock 函数 加锁 锁-- 阻塞线程

pthread_mutex_unlock 函数 解锁p 锁++ 唤醒阻塞在锁上的线程

pthread_mutex_t lock; 创建锁

以上函数成功返回0,失败返回errno,pthread_mutex_t类型,本质是结构体,可以当成整数看待(1/0)

锁的注意事项:

锁的粒度越小越好,访问共享数据前加锁,访问结束立即解锁

pthread_trylock函数

尝试加锁,成功则--,失败返回设置错误号

死锁

一种使用锁不恰当导致的现象:

1. 对一个锁反复lock

2. 线程1拥有A锁,请求获得B锁,线程2拥有B锁,请求获得A锁

读写锁

并行性相比互斥锁更高

特性:锁只有一把,以读方式给数据加锁为读锁,以写方式给数据加锁为写锁,读共享,写独占,写锁优先级高(排队阻塞时)

相关函数:

pthread_rwlock_t  rwlock;

pthread_rwlock_init(&rwlock, NULL);

pthread_rwlock_rdlock(&rwlock);  /  pthread_rwlock_tryrdlock(&rwlock);

pthread_rwlock_wrlock(&rwlock);  /  pthread_rwlock_trywrlock(&rwlock);

pthread_rwlock_unlock(&rwlock);

pthread_rwlock_destroy(&rwlock);

以上函数都是成功返回0,失败返回错误号。

pthread_rwlock_t 类型 用于定义一个读写锁变量

pthread_rwlock_t rwlock;

示例代码,一个全局资源,三个写进程,五个读进程 :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>

int counter;
pthread_rwlock_t rwlock; 

void *th_write(void *arg){	
	int t;
	int i = (int)arg;
	while(1){
		t = counter;
		usleep(1000);
		pthread_rwlock_wrlock(&rwlock);
		printf("====write %d: %lu: counter = %d ++counter=%d\n", i, pthread_self(), t, ++counter); 
		pthread_rwlock_unlock(&rwlock); 
		usleep(9000);
	}
	return NULL; 
}

void *th_read(void *arg){
	int i = (int)arg;
	while(1){
		pthread_rwlock_rdlock(&rwlock);
		printf("----read %d: %lu: %d\n", i, pthread_self(),counter);
		pthread_rwlock_unlock(&rwlock);
		usleep(2000);
	}
	return NULL; 

}

int main(int argc, char *argv[]){
	int i; 
	pthread_t tid[8];
	pthread_rwlock_init(&rwlock, NULL);
	for(i=0;i<3;i++)
		pthread_create(&tid[i], NULL, th_write, (void*)i);
	for(i=0;i<5;i++)
		pthread_create(&tid[i+3], NULL, th_read, (void*)i);
	for(i=0;i<8;i++)
		pthread_join(tid[i], NULL);

	pthread_rwlock_destroy(&rwlock);
	return 0; 
}

结果如下:

条件变量

本身不是锁,通常结合互斥锁来使用。

主要函数:

pthread_cond_init函数

pthread_cond_destroy函数

pthread_cond_wait函数

pthread_cond_timedwait函数

pthread_cond_signal函数

pthread_cond_broadcast函数

以上函数返回值:成功0失败返回错误号

初始化条件变量:

1. pthread_cond_init(&cond, NULL);    动态初始化。

2. pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 静态初始化。

pthread_cond_wait函数

原型: int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

作用:

1. 阻塞等待条件变量满足

2. 解锁已经加锁成功的信号量 (相当于 pthread_mutex_unlock(&mutex)),12两步为一个原子操作

3. 当条件满足,函数返回时,解除阻塞并重新申请获取互斥锁。重新加锁信号量 (相当于, pthread_mutex_lock(&mutex);)

生产者消费者模型

示例代码,使用条件变量模拟生产者消费者问题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>

struct msg{
	struct msg *next;
	int num; 
};

struct msg *head; 
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void err_thread(int ret, char *str){
	if(ret!=0){
		fprintf(stderr, "%s:%s\n", str, strerror(ret)); 
		pthread_exit(NULL); 
	}
}


void *consumer(void *arg){
	struct msg *mp;
	for(;;){
		pthread_mutex_lock(&lock); 	
		while(head==NULL){
			pthread_cond_wait(&has_product, &lock); 
		}
	
		mp = head; 
		head = mp->next; 
		pthread_mutex_unlock(&lock); 
		printf("-Consume %lu---%d\n", pthread_self(), mp->num); 
		free(mp); 
		sleep(rand()%5); 
	}
}

void *producer(void *p){
	struct msg *mp;
	for(;;){
		mp = malloc(sizeof(struct msg)); 
		mp->num = rand() % 1000 + 1; 
		printf("-Produce ---%d\n", mp->num); 
		pthread_mutex_lock(&lock); 
		mp->next = head; 
		head = mp; 
		pthread_mutex_unlock(&lock); 
		pthread_cond_signal(&has_product); 
		sleep(rand() % 5); 
	}

}

int main(int argc, char *argv[]){
	int i, ret; 
	pthread_t pid, cid;
	srand(time(NULL)); 
	ret = pthread_create(&pid, NULL, producer, NULL);
	if(ret!=0){
		err_thread(ret, "pthread_create producer error"); 	
	}
	ret = pthread_create(&cid, NULL, consumer, NULL); 
	if(ret!=0){
		err_thread(ret, "pthread_create producer error"); 
	}
	pthread_join(pid, NULL);
	pthread_join(cid, NULL); 
	return 0; 
}

结果如下, 消费者按链表顺序消费:

 

多个消费者的示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>

struct msg{
	struct msg *next;
	int num; 
};

struct msg *head; 
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void err_thread(int ret, char *str){
	if(ret!=0){
		fprintf(stderr, "%s:%s\n", str, strerror(ret)); 
		pthread_exit(NULL); 
	}
}


void *consumer(void *arg){
	struct msg *mp;
	for(;;){
		pthread_mutex_lock(&lock); 	
		while(head==NULL){
			pthread_cond_wait(&has_product, &lock); 
		}
	
		mp = head; 
		head = mp->next; 
		pthread_mutex_unlock(&lock); 
		printf("-Consume id=%lu---%d\n", pthread_self(), mp->num); 
		free(mp); 
		sleep(rand()%5); 
	}
}

void *producer(void *p){
	struct msg *mp;
	for(;;){
		mp = malloc(sizeof(struct msg)); 
		mp->num = rand() % 1000 + 1; 
		printf("-Produce ---%d\n", mp->num); 
		pthread_mutex_lock(&lock); 
		mp->next = head; 
		head = mp; 
		pthread_mutex_unlock(&lock); 
		pthread_cond_signal(&has_product); 
		sleep(rand() % 5); 
	}

}

int main(int argc, char *argv[]){
	int i, ret; 
	pthread_t pid, cid;
	srand(time(NULL)); 
	ret = pthread_create(&pid, NULL, producer, NULL);
	if(ret!=0){
		err_thread(ret, "pthread_create producer error"); 	
	}
	for(i=0; i<5; i++){
		ret = pthread_create(&cid, NULL, consumer, NULL); 
		if(ret!=0){
			err_thread(ret, "pthread_create producer error"); 
		}
	}
	pthread_join(pid, NULL);
	pthread_join(cid, NULL); 
	return 0; 
}

注意,消费者阻塞在条件变量时应该使用while循环判断,这样在一个消费者消费完数据之后,另外一个做的第一件事不是拿锁而是去判断条件变量

信号量

信号量相当于初始化值为n的互斥量,既能保证同步,数据不混乱,又能提高线程并发。

函数:

sem_t sem; 定义类型

int sem_init(sem_t *sem, int pshared, unsigned int value); 初始化

参数:

sem: 信号量

pshared: 0: 用于线程间同步

1: 用于进程间同步

value:N值。(指定同时访问的线程数)

sem_destroy(); 销毁

sem_wait(); 一次调用,做一次-- 操作, 当信号量的值为 0 时,再次 -- 就会阻塞。 (对比 pthread_mutex_lock)

sem_post(); 一次调用,做一次++ 操作. 当信号量的值为 N 时, 再次 ++ 就会阻塞。(对比 pthread_mutex_unlock)

定义两个信号量空格数和产品数,实现生产者消费者模型:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h> 

#define NUM 5

int queue[NUM];
sem_t blank_number, product_number; 


void *producer(void *arg){
	int i = 0;
	while(1){
		sem_wait(&blank_number);
		queue[i] = rand() % 1000 + 1; 
		printf("---Produce---%d\n", queue[i]);
		sem_post(&product_number);
		i = (i+1) % NUM;
		sleep(rand()%1);
	}
}

void *consumer(void *p){
	int i = 0; 
	while(1){
		sem_wait(&product_number); 
		printf("---Consume---%d\n", queue[i]);
		queue[i] = 0; 
		sem_post(&blank_number);
		i = (i+1) % NUM;
		sleep(rand()%3);
	}

}

int main(int argc, char *argv[]){ 
	pthread_t pid, cid;
	sem_init(&blank_number, 0, NUM);
	sem_init(&product_number, 0, 0);

	pthread_create(&pid, NULL, producer, NULL);
	pthread_create(&cid, NULL, consumer, NULL); 
	
	pthread_join(pid, NULL);
	pthread_join(cid, NULL);
	
	sem_destroy(&blank_number);
	sem_destroy(&product_number);
	return 0; 
}

结果如下:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值