【操作系统理论】——线程同步

互斥锁

举例:喂鱼问题
Alice和Tom,只为喂一次鱼

/*方法一*/
// Alice
lock()
if (no feed)
{
	feed fish
}
unlock()

// Tom
lock()
if (no feed)
{
	feed fish
}
unlock()

互斥锁的上锁和解锁

上锁和解锁都是原子操作
上锁

  • 等待锁至打开状态
  • 获得锁并锁上

解锁

在这里插入图片描述
在这里插入图片描述

信号量

简介

  • 信号量是一个计数器,与其它进程间通信方式不大相同,它主要用于控制多个进程间或一个进程内的多个线程间对共享资源的访问,相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同 时,进程也可以修改该标志,除了用于共享资源的访问控制外,还可用于进程同步。

  • 它常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源,因此,主要作为进程间以及同一个进程内不同线程之间的同步手段

  • 信号量是一个整数,除了初始化时可以被赋值外,只能通过P、V操作对其进行操作

信号量的实现

在这里插入图片描述

P(s)
{
	while (s <= 0) 
	{
		do nothing
	}
	s--;
}
V(s)
{
	s++;
}

信号量使用

二值信号量

简介

二值信号量的值只能是0或1,通常将其初始化为1,用于实现互斥锁的功能。

semaphore mutex = 1;
process Pi {
	P(mutex);
	Critical section
	V(mutex);
}

一般信号量

一般信号量的取值可以是任意数值,用于控制并发进程/线程对共享资源的访问。
在这里插入图片描述
在这里插入图片描述

使用

#include <semaphore.h>

sem_t:信号量的数据类型
/*
* sem: 信号量
* pshared:信号量的类别,0表示进程内的线程都可用,非0表示不同进程间可用
* val: 信号量的初始值
*/
int sem_init(sem_t *sem, int pshared, unsigned int val);

/*
* 相当于P操作
*/
int sem_wait(sem_t *sem);

/*
* 相当于V操作
*/
int sem_post(sem_t *sem);

/*
* 销毁信号量
*/
int sem_destroy(sem_t *sem);
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static sem_t s;

void *handler_p(void *data)
{
        sem_wait(&s);
        printf("(%lu) I INTEND to pass the fork\n", pthread_self());
        sleep(1);
        printf("(%lu) I AM  AT the fork\n", pthread_self());
        sleep(1);
        printf("(%lu) I haved  passed the fork\n", pthread_self());
        sleep(1);
        sem_post(&s);
        pthread_exit(NULL);
}


int main()
{
        sem_init(&s, 0, 3);
        int i = 0;
        pthread_t p1[5];
        for (i = 0; i<5;i++){
                pthread_create(&p1[i], NULL, handler_p, NULL);
        }

        for (i = 0; i<5;i++){
                pthread_join(p1[i], NULL);
        }  

        sem_destroy(&s);
        pthread_exit(NULL);
}

信号量实现同步

  • 同步问题实质是将异步的并发进程按照某种顺序执行
  • 解决同步的本质是找到并发进程的交互点,利用P操作的等待特点来调节进程的执行速度
  • 通常初始值为0的信号量可以让进程直接进行等待状态,知道另一个进程唤醒它。
  • 初始值=1,相当于互斥锁
  • 初始值>1,相当于可用资源数量
  • 初始值=0,相当于同步问题
经典的同步问题
生产者-消费者问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

条件变量用于生产者-消费者问题
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
static pthread_mutex_t mutex; //定义互斥锁
static pthread_cond_t cond; //定义条件变量
static int g_avail = 0; //全局共享资源
/* 消费者线程 */
static void *consumer_thread(void *arg)
{
	for ( ; ; ) {
		pthread_mutex_lock(&mutex);//上锁
		while (0 >= g_avail)
			pthread_cond_wait(&cond, &mutex);//等待条件满足
		while (0 < g_avail)
			g_avail--; //消费
		pthread_mutex_unlock(&mutex);//解锁
	}
	return (void *)0;
}
/* 主线程(生产者) */
int main(int argc, char *argv[])
{
	pthread_t tid;
	int ret;
	/* 初始化互斥锁和条件变量 */
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
	/* 创建新线程 */
	ret = pthread_create(&tid, NULL, consumer_thread, NULL);
	
	if (ret) {
		fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
		exit(-1);
	}
	for ( ; ; ) {
		pthread_mutex_lock(&mutex);//上锁
		g_avail++; //生产
		pthread_mutex_unlock(&mutex);//解锁
		pthread_cond_signal(&cond);//向条件变量发送信号
	}
	exit(0);
}
苹果橘子问题

在这里插入图片描述
在这里插入图片描述

读写问题

在这里插入图片描述
在这里插入图片描述

死锁

在这里插入图片描述

产生死锁的四个必要条件

  • 互斥使用:一个时刻,一个资源只能被一个进程使用
  • 不可剥夺:除了资源占有进程主动释放资源,其他进程都不可抢夺其资源
  • 占用和等待:一个进程的请求资源得不到满足等待时,不释放已经占用的资源
  • 循环等待:每一个进程分别等待它前一个进程所占用的资源

死锁的解决方案

  • 死锁的防止:破坏四个必要条件之一
  • 死锁的避免:允许四个必要条件同时存在,在并发进程中妥善安排避免死锁的发生
  • 死锁的检测和恢复:允许死锁的发生,系统及时检测并解除它

死锁的避免

在这里插入图片描述

死锁的检测和恢复

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值