定义PV操作的含义:PV操作是由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):将信号量S的值减1,即S=S-1;如果S>0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):将信号量S的值加1,即S=S+1;如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
P操作相当于申请资源,而V操作相当于释放资源。
一、读者-写者问题
计算机系统中的数据(文件、记录)常被多个进程共享,但其中某些进程可能只要求读数据(称为读者Reader),另一些进程则要求修改数据(称为写者Writer)。就共享数据而言,Reader和Writer是两组并发进程共享一组数据区,要求:
(1)允许多个读者同时执行读操作;
(2)不允许读者、写者同时操作;
(3)不允许多个写者同时操作。
Reader和Writer的同步问题分为读者优先、弱写者优先(公平竞争)和强写者优先三种情况,它们的处理方式不同:
1、读者优先
int rc = 0; //记录有多少个读者在读
semaphore rc_semaphore = 1; //对rc互斥访问的信号量
semaphore write_semaphore = 1; //保证读写互斥的信号量
void reader()
{
do
{
P(rc_semaphore);
rc++;
if(1==rc)
P(write_semaphore);
V(rc_semaphore);
read operating.....
P(rc_semaphore);
rc--;
if(0==rc)
V(write_semaphore);
v(rc_semaphore);
} while (true);
}
void writer()
{
do
{
P(write_semaphore);
write operating.....
V(write_semaphore);
} while (true);
}
缺点:当不断有读者来读取时,写者就会陷入无限的等待。
2、写者优先(弱优先性)
int rc = 0; //记录有多少个读者在读
semaphore rc_semaphore = 1; //对rc互斥访问的信号量
semaphore write_semaphore = 1; //保证读写互斥的信号量
semaphore read_semaphore = 1;
void reader()
{
do
{
P(read_semaphore);
P(rc_semaphore);
rc++;
if(1==rc)
P(write_semaphore);
V(rc_semaphore);
V(read_semaphore);
Reading the file....
P(rc_semaphore);
rc--;
if(0==rc)
V(write_semaphore);
V(rc_semaphore);
} while (true);
}
void writer()
{
do
{
P(read_semaphore);
P(write_semaphore);
writing the file.....
V(write_semaphore);
V(read_semaphore);
} while (true);
}
缺点:这个是按照先来先服务的原则进行读写的,比如当一个进程在写时,来了N个进程读,读者们都在等写者结束,此时又来了个写者,第二个写者就要等前面N个读者读完后才能写。3、写者优先(强优先性)
int rc = 0;
int wc = 0;
semaphore write_semaphore = 1;
semaphore rc_semaphore =1;
semaphore wc_semaphore = 1;
void reader()
{
do
{
P(read_semaphore);
P(rc_semaphore);
rc++;
if(1==rc)
P(write_semaphore);
V(rc_semaphore);
V(read_semaphore);
reading the file.....
P(rc_semaphore);
rc--;
if(0==rc)
V(write_semaphore);
V(rc_semaphore);
} while (true);
}
void writer()
{
do
{
P(wc_semaphore);
wc++;
if(1==wc)
P(read_semaphore);
V(wc_semaphore);
P(write_semaphore);
writing the file......
V(write_semaphore);
P(wc_semaphore);
wc--;
if(0==wc)
V(read_semaphore);
V(wc_semaphore);
} while (true);
}
保证了写者优先,只要有写者在写,后来的读者写者中优先执行写者。
二、生产者-消费者
生产者-消费者问题指的是在若干个生产者生产产品,放入共享的缓冲区,若干消费者从缓冲区中取出产品消费;生产者和消费者是互斥访问共享的缓冲区的,若缓冲区没有空位生产者要等待,若缓冲区没有产品消费者要等待,以下通过代码来演示这个过程。
注:以下代码修改自http://blog.sina.com.cn/s/blog_58069bd20100as5w.html
#include <windows.h>
#include <iostream>
using namespace std;
const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度
unsigned short ProductID = 0; //产品号
unsigned short ConsumeID = 0; //将被消耗的产品号
unsigned short in = 0; //产品进缓冲区时的缓冲区下标
unsigned short out = 0; //产品出缓冲区时的缓冲区下标
int g_buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列
HANDLE g_hMutex; //用于线程间的互斥
HANDLE g_hFullSemaphore; //当缓冲区满时迫使生产者等待
HANDLE g_hEmptySemaphore; //当缓冲区空时迫使消费者等待
DWORD WINAPI Producer(LPVOID); //生产者线程
DWORD WINAPI Consumer(LPVOID); //消费者线程
int main()
{
//创建各个互斥信号
g_hMutex = CreateMutex(NULL,FALSE,NULL);
g_hFullSemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
g_hEmptySemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL);
//调整下面的数值,可以发现,当生产者个数多于消费者个数时,
//生产速度快,生产者经常等待消费者;反之,消费者经常等待
const unsigned short PRODUCERS_COUNT = 3; //生产者的个数
const unsigned short CONSUMERS_COUNT = 2; //消费者的个数
//总的线程数
const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
HANDLE hThreads[THREADS_COUNT]; //各线程的handle
DWORD producerID[PRODUCERS_COUNT]={1,2,3}; //生产者线程的标识符
DWORD consumerID[CONSUMERS_COUNT]={1,2}; //消费者线程的标识符
//创建生产者线程
for (int i=0;i<PRODUCERS_COUNT;++i){
hThreads[i]=CreateThread(NULL,0,Producer,&producerID[i],0,NULL);
if (hThreads[i]==NULL) return -1;
}
//创建消费者线程
for (int i=0;i<CONSUMERS_COUNT;++i){
hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,&consumerID[i],0,NULL);
if (hThreads[i]==NULL) return -1;
}
while(true){
Sleep(10000);
}
return 0;
}
//生产一个产品。简单模拟了一下,仅输出新产品的ID号
void Produce()
{
std::cerr << "Producing " << ++ProductID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//把新生产的产品放入缓冲区
void Append()
{
std::cerr << "Appending a product ... ";
g_buffer[in] = ProductID;
in = (in+1)%SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
for (int i=0;i<SIZE_OF_BUFFER;++i){
std::cout << i <<": " << g_buffer[i];
if (i==in) std::cout << " <-- 生产";
if (i==out) std::cout << " <-- 消费";
std::cout << std::endl;
}
}
//从缓冲区中取出一个产品
void Take()
{
std::cerr << "Taking a product ... ";
ConsumeID = g_buffer[out];
g_buffer[out] = 0;
out = (out+1)%SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
for (int i=0;i<SIZE_OF_BUFFER;++i){
std::cout << i <<": " << g_buffer[i];
if (i==in) std::cout << " <-- 生产";
if (i==out) std::cout << " <-- 消费";
std::cout << std::endl;
}
}
//消耗一个产品
void Consume()
{
std::cerr << "Consuming " << ConsumeID << " ... ";
std::cerr << "Succeed" << std::endl;
}
//生产者
DWORD WINAPI Producer(LPVOID lpPara)
{
while(true){
WaitForSingleObject(g_hEmptySemaphore,INFINITE);
WaitForSingleObject(g_hMutex,INFINITE);
Produce();
Append();
Sleep(3000);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hFullSemaphore,1,NULL);
}
return 0;
}
//消费者
DWORD WINAPI Consumer(LPVOID lpPara)
{
while(true){
WaitForSingleObject(g_hFullSemaphore,INFINITE);
WaitForSingleObject(g_hMutex,INFINITE);
Take();
Consume();
Sleep(3000);
ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hEmptySemaphore,1,NULL);
}
return 0;
}
三、哲学家进餐
问题描述:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子,哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。关键问题是:能为每一个哲学家写一段描述其行为的程序,且决不会死锁吗(最好能达到最大的并行程度)?
解法思路:
1、为每个哲学家分配一个semaphore[i]初始值为0,最大值为1
2、当一个哲学家试图进餐时先通过互斥量获得对哲学家们的状态state的独占性访问
3、改变i哲学家的状态为HUNGRY,测试其左右哲学家是否在用餐,若左右哲学家都不在用餐,则i哲学家可以用餐,改变state[i]=EATING,释放其semaphore[i],释放state,拿起餐插成功进餐;若左右哲学家有人在进餐,释放state,i哲学家等待
4、哲学家用餐完毕,获取对state的独占性访问,改变state[i]=THINKING,此时i哲学家释放了两个餐插,测试其左右哲学家是否在HUNGRY状态
5、重复2-4步骤直到达到最大进餐次数
参考如下代码:(以下代码修改自http://blog.163.com/diaoshuo_1/blog/static/318902012009418115121514/)
/*
Item The Dining Philosophers Problem
具体要求
1) 5个哲学家,先吃饭,后思考。
2) 每个哲学家都需要吃9顿饭,思考9次,然后结束。
3)吃饭时间为3~7秒的随机时间
4)思考时间为3~9秒的随机时间
*/
#include <windows.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define N 5 //哲学家数目
#define MAXNUM 9 //进餐思考次数
#define LEFT (i+N-1)%N //i为左邻居编号
#define RIGHT (i+N+1)%N //i为右邻居编号
#define THINKING 0 //哲学家在思考
#define HUNGRY 1 //哲学家视图拿起叉子
#define EATING 2 //哲学家进餐
///线程参数
typedef struct _THREAD_ARGS{
int id;//哲学家编号
int maxnum; //进餐思考最大次数
}THREAD_ARGS, *PTHREAD_ARGS;
int state[N];//用来记录每位哲学家的状态
int g_philobuf_fork_num;//缓冲区中叉子数目
char mutex_g_buf[]="PS_IPC_PHILO_G_BUF";
HANDLE mutex_g_buf_h; //为了互斥访问state[N]
//Semaphores' Name
char* sem_perphilo_name[N] = {"PS_IPC_PHILO_SEM_PERPHILO_0","PS_IPC_PHILO_SEM_PERPHILO_1",
"PS_IPC_PHILO_SEM_PERPHILO_2","PS_IPC_PHILO_SEM_PERPHILO_3",
"PS_IPC_PHILO_SEM_PERPHILO_4"};
HANDLE sem_perphilo_h[N]; //每个哲学家一个semaphore,初始值为0,最大值为1
DWORD WINAPI philosopher(void *pArgs);
void take_forks(int i);
void put_forks(int i);
void test(int i);
//线程函数
DWORD WINAPI philosopher(void *pArgs)
{
PTHREAD_ARGS pTHREAD_ARGS;
pTHREAD_ARGS=(PTHREAD_ARGS)pArgs;
int i=pTHREAD_ARGS->id;
int totalNum = pTHREAD_ARGS->maxnum;
for(int count=0;count<totalNum;count++)
{
take_forks(i);
srand ((DWORD)time(NULL));
int eat_t=rand()%4000 + 3000;
Sleep(eat_t);
printf("Philosopher %d finishes eating!this is the %d(th) eating time! eat_t:%d\n",i,count+1,eat_t);
put_forks(i);
srand ((DWORD)time(NULL));
int think_t= rand()%6000 + 3000;
Sleep(think_t);
printf("Philosopher %d finishes thinking!this is the %d(th) thinking time! think_t:%d\n",i,count+1,think_t);
}
printf("Philosopher %d has finished all the eating and thinking activity\n!",i);
return 0;
}
void take_forks(int i)
{
WaitForSingleObject(mutex_g_buf_h,INFINITE);
state[i]=HUNGRY;
printf("*************************take_forks start***************************\n");
printf("Philosopher %d is hungry! Try to take the forks\n",i);
printf("*************************take_forks end***************************\n\n");
test(i);
ReleaseMutex(mutex_g_buf_h);
WaitForSingleObject(sem_perphilo_h[i],INFINITE);
}
void put_forks(int i)
{
WaitForSingleObject(mutex_g_buf_h,INFINITE);
state[i]=THINKING;
g_philobuf_fork_num=g_philobuf_fork_num-2;//放下了两把叉子
printf("*************************put_forks start***************************\n");
printf("Philosopher %d puts down the fork and starts thinking! current forks num:%d\n",i,g_philobuf_fork_num);
printf("*************************put_forks end***************************\n\n");
test(LEFT);
test(RIGHT);
ReleaseMutex(mutex_g_buf_h);
}
void test(int i)
{
LONG oldSemValue;
if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING)
{
state[i]=EATING;
g_philobuf_fork_num=g_philobuf_fork_num+2;//拿起了两把叉子
printf("*************************test start***************************\n");
printf("After testing,Philosopher %d can start