zoukankan      html  css  js  c++  java
  • windows多线程(九) PV原语分析同步问题

    一、PV原语介绍

    PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。

    信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2) semaphore的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数。

    信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语(P、V原语)来访问。初始化可指定一个非负整数,即空闲资源总数。

    二、PV原语原理

    PV操作由P操作原语和V操作原语组成(原语也叫原子操作Atomic Operation,是不可中断的过程),对信号量(注意不要和Windows中的信号量机制相混淆)进行操作,具体定义如下:

    PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。

    P(S):
    
    ①将信号量S的值减1,即S=S-1;
    
    ②如果S>=0,则该进程继续执行;否则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
    
    
    V(S):
    
    ①将信号量S的值加1,即S=S+1;
    
    ②该进程继续执行;如果该信号的等待队列中有等待进程就唤醒一等待进程。
    

    具体PV原语对信号量的操作可以分为三种情况:

    1. 把信号量视为一个加锁标志位,实现对一个共享变量的互斥访问。实现过程如下:
      P(mutex); // mutex的初始值为1 访问该共享数据;
      V(mutex);
      非临界区

    2. 把信号量视为是某种类型的共享资源的剩余个数,实现对一类共享资源的访问。实现过程如下:
      P(resource); // resource的初始值为该资源的个数N 使用该资源;
      V(resource);
      非临界区

    3. 把信号量作为进程间的同步工具(???)。实现过程如下:
      临界区C1;
      P(S);
      V(S);
      临界区C2;

    三、实例分析

    (一) 题目

    桌上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放橘子,儿子专等吃盘中的橘子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用,请用P、V原语实现爸爸、儿子、女儿三个并发进程的同步。

    (二) 过程分析

    • 先考虑同步情况即所有“等待”情况:
      第一.爸爸要等待盘子为空。
      第二.儿子要等待盘中水果是橘子。
      第三.女儿要等待盘中水果是苹果。

    • 看起来盘子好像是要作互斥处理的,但由于题目中的爸爸、儿子、女儿均只有一个,并且他们访问盘子的条件都不一样,所以他们根本不会同时去访问盘子,因此盘子也就不用作互斥处理了。

    • 先设置三个信号量,信号量Orange表示盘中有橘子,初值为0。信号量Apple表示盘中有苹果,初值为0。信号量EmptyDish表示盘子为空,初值为1。

    1.爸爸
    P(EmptyDish)
    if (rand()%2==0)
    {
    放橘子
    V(Orange)
    }
    else
    {
    放苹果
    V(Apple)
    }

    2.儿子
    P(Orange)
    取橘子
    V(EmptyDish)

    3.女儿
    P(Apple)
    取苹果
    V(EmptyDish)

    (三) 代码实现

    
    
    #include <iostream>
    #include<windows.h>
    using namespace std;
    
    DWORD WINAPI  FatherThread(LPVOID);  //爸爸往盘子中放橘子或苹果的线程
    DWORD WINAPI  DaughterThread(LPVOID); //女儿取走苹果线程
    DWORD WINAPI  SonThread(LPVOID);    //儿子取走橘子线程
    
    HANDLE g_Disk, g_Orange, g_Apple;
    
    
    int main()
    {
    	g_Disk = CreateSemaphore(NULL, 1, 1, NULL);   //初始化盘子信号量,设置盘子为空
    	g_Orange = CreateSemaphore(NULL, 0, 1, NULL);
    	g_Apple = CreateSemaphore(NULL, 0, 1, NULL);
    
    	//启动三个线程
    	const int THREAD_NUM = 3;
    	HANDLE handle[THREAD_NUM];
    	handle[0] = CreateThread(NULL, 0, FatherThread, NULL, 0, NULL);
    	handle[1] = CreateThread(NULL, 0, DaughterThread, NULL, 0, NULL);
    	handle[2] = CreateThread(NULL, 0, SonThread, NULL, 0, NULL);
    
    	getchar();  //不阻塞输入,用来结束程序
    	CloseHandle(g_Disk);
    	CloseHandle(g_Apple);
    	CloseHandle(g_Orange);
    	for (int i = 0; i < THREAD_NUM; i++)
    	{
    		CloseHandle(handle[i]);
    	}
    	return 0;
    }
    
    
    DWORD WINAPI  FatherThread(LPVOID)
    {
    	while (1)
    	{
    		WaitForSingleObject(g_Disk, INFINITE);
    		Sleep(1000);
    		if (rand() % 2 == 0)
    		{
    			cout << "爸爸向盘子中放入了一个橘子!" << endl;
    			ReleaseSemaphore(g_Orange, 1, NULL);
    		}
    		else
    		{
    			cout << "爸爸向盘子中放入了一个苹果!" << endl;
    			ReleaseSemaphore(g_Apple, 1, NULL);
    		}
    	}
    
    
    	return 0;
    }
    
    DWORD WINAPI  DaughterThread(LPVOID)
    {
    	while (1)
    	{
    		WaitForSingleObject(g_Apple, INFINITE);
    		Sleep(1000);
    		cout << "			女儿取走了盘子中的苹果!" << endl;
    		ReleaseSemaphore(g_Disk, 1, NULL);
    	}
    
    	return 0;
    }
    
    DWORD WINAPI  SonThread(LPVOID)
    {
    	while (1)
    	{
    		WaitForSingleObject(g_Orange, INFINITE);
    		Sleep(1000);
    		cout << "			儿子取走了盘子中的橘子!" << endl;
    		ReleaseSemaphore(g_Disk, 1, NULL);
    	}
    
    	return 0;
    }
    
    
    

    运行结果如下所示:

  • 相关阅读:
    N*N内递矩阵算法
    四舍五入算法C语言
    springboot整合hivejdbc遇到的坑
    Gradle 中 exclude group 关键字运用
    sparklocal运行异常Could not locate executable null\bin\winutils.exe in the Hadoop binaries
    大数据的五大关键技术
    opencv+python+pycharm实现人脸识别
    Python开源人脸识别库,识别率达99.38%!内附教程+源码分享
    Python实时语音识别控制
    JAVA操作鼠标和键盘
  • 原文地址:https://www.cnblogs.com/ay-a/p/9130562.html
Copyright © 2011-2022 走看看