操作系统实验(3)—— 典型同步问题模拟处理编程设计与实现
📖 操作系统实验—— 典型同步问题模拟处理编程设计与实现文章目录Lab6: 典型同步问题模拟处理编程设计与实现1. 实验目的2. 实验要求3. 实验内容3.1 生产者-消费者问题3.1.1 问题描述3.1.2 编程实现3.1.3结果分析3.2 读者写者问题(读者优先、写者优先)3.2.1 问题描述3.2.2 编程实现3.2.2.1 读者优先3.2.2.2 写者优先3.2.3 结果分析3.3 哲学
📖 操作系统实验—— 典型同步问题模拟处理编程设计与实现
Lab6: 典型同步问题模拟处理编程设计与实现
1. 实验目的
探索、理解并掌握操作系统同步机制的应用编程方法,针对典型的同步问题,构建基于Windows(或 Linux)操作系统同步机制的解决方案。
2. 实验要求
了解、熟悉和运用 Windows(或 Linux)操作系统同步机制及编程方法,针对典型的同步问题,譬如生产者-消费者问题、读者优先的读者-写者问题、写者优先的读者-写者问题、读者数限定的读者-写者问题、哲学家就餐问题等(任选四个即可),编程模拟实现相应问题的解决方案。
3. 实验内容
3.1 生产者-消费者问题
3.1.1 问题描述
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品(数据)并使用。
- 生产者、消费者共享一个初始为空、大小为n的缓冲区。
- 只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。
- 有缓冲区不空时,消费者才能从中取出产品,否则必须等待。
- 缓冲区是临界资源,各进程必须互斥地访问。
伪代码描述:
semaphore mutex = 1; // 互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; // 同步信号量,表示空闲缓冲区的数量
semaphore full = 0; // 同步信号量,表示非空缓冲区的数量
// 生产者
producer(){
while(1){
ProduceProduct();//生产一个产品
P(empty); // 消耗一个空闲缓冲区
P(mutex); // 缓冲区互斥访问
PutProduct();//把产品放入缓冲区
V(mutex); // 缓冲区互斥访问
V(full); // 增加一个产品
}
}
// 消费者
consumer(){
while(1){
P(full); //消耗一个产品
P(mutex); // 缓冲区互斥访问
GetProduct(); // 从缓冲区中取出一个产品
V(mutex); // 缓冲区互斥访问
UseProduct(); // 使用产品
}
}
3.1.2 编程实现
首先设置信号量用于进程的同步和互斥,具体实现如下:
int n = 10; // 单个缓存区大小
int in[3] = { 0,0,0 }, out[3] = { 0,0,0 }; // 生产指针,消费指针
int flag[3] = { 0,0,0 }; // 分别对应三个缓冲区所存放的数据个数,初始时都为0
int buffer[3][10]; // 3个缓存区(分别是1,2,3)每个缓冲区可以存放10个数据
mutex mtx[3]; // 缓冲区互斥量
mutex data_mtx; // 输出数据锁
然后实现生产者函数,代码和注释如下:
void producer(int id)
{
while(1){
for (int i = 0; i < 3; i++) // 依次遍历三个缓冲区,看哪一个缓冲区满足写条件
{
if (mtx[i].try_lock()) // 如果可以对第i个缓冲区上锁
{
if (count[i]!=n) // 且第i个缓冲区不满
{
buffer[i][in[i]] = 1; // 第i个缓冲区写指针对应的位置写为1
display_mtx.lock(); // 输出前上锁
cout << "【Producer" << id << "】: is producing 【product" << in[i] <<"】at 【Buffer"<<i+1 <<"】"<< endl;
cout << "【Buffer"<<i+1 << "】"<<":";
for (int j = 0; j < n; j++)
{
cout << buffer[i][j] << " "; // 遍历输出第i个缓冲区的值
}
cout<<endl<<"---------------------------------------------------------------------"<<endl;
display_mtx.unlock();// 输出后解锁
in[i] = (in[i] + 1) % n; // 第i个缓冲区的写指针循环遍历
count[i]++; // 第i 个缓冲区的个数加1
mtx[i].unlock(); // 对第i个缓冲区解锁
this_thread::sleep_for(chrono::seconds(1)); // 延迟1s
break;
}
else // 第i个缓冲区已满
{
mtx[i].unlock(); // 对第i个缓冲区解锁
}
}
}
}
}
然后实现生产者函数,代码和注释如下:
void consumer(int id)
{
while(1){
for (int i = 0; i < 3; i++) // 依次遍历三个缓冲区,看哪一个缓冲区满足读条件
{
if (mtx[i].try_lock()) // 试探第i个缓冲区,并上锁
{
if (count[i] != 0) // 如果第i个缓冲区不为空
{
buffer[i][out[i]] = 0; // 将都指针指向的位置写入0,即消费
display_mtx.lock(); // 写数据上锁
cout << "【Consumer" << id << "】: is consuming 【product" << out[i] <<"】from 【Buffer"<<i+1 <<"】"<< endl;
cout << "【Buffer"<<i+1 << "】" << ":";
for (int j = 0; j < n; j++)
{
cout << buffer[i][j] << " "; // 输出第i个缓冲区的值
}
cout<<endl<<"---------------------------------------------------------------------"<<endl;
display_mtx.unlock(); // 写数据解锁
out[i] = (out[i] + 1) % n; // 读指针后移
count[i]--;// 第i个缓冲区的个数减1
mtx[i].unlock(); // 缓冲区解锁
this_thread::sleep_for(chrono::seconds(5)); // 等待5s
break;
}
else // 第i个缓冲区为空,则解锁
{
mtx[i].unlock();
}
}
}
} ;
}
3.1.3 结果分析
运行环境:本地Windows电脑上的CLion编译器
运行结果如下图所示,生产者和消费者之间互斥的进行临界资源的访问,且当缓冲区满时,则无法继续生产;当缓冲区为空时,则无法继续消费。
3.2 读者写者问题(读者优先、写者优先)
3.2.1 问题描述
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。读者写者问题基本要求如下:
-
允许多个读者可以同时对文件执行读操作(读读允许)
-
只允许一个写者往文件中写信息(写写互斥)
-
任一写者在完成写操作之前不允许其他读者或写者工作(读写互斥)
-
读者优先
附加条件:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。
伪代码描述如下:
/* 读者优先 */
//定义信号量
semaphore mutex =1; // 对R_count的互斥操作
semaphore RP_Write=1; // 保证读者优先,读写互斥
int R_Count=0; // 当前写者的数量
// 读者优先——读者
Reader(){
while(1){
wait(mutex); // 上锁,防止多个进程同时操作R_count
R_Count++;// 读者数量加1
if(R_Count==1) // 如果当前只有一个读者进程
wait(RP_Write); // 则该进程负责上锁,不允许写进程执行
signal(mutex); // 释放锁,允许其他进程操作R_count
readFile();//读临界区
wait(mutex);// 上锁,防止多个进程同时操作R_count
R_Count--;// 读者数量减1
if(R_count==0) // 如果当前读者进程数为0
signal(RP_Write); // 则最后一个离开的进程负责解锁,此时允许写进程执行
signal(mutex); // 释放锁,允许其他进程操作R_count
}
}
//写者优先——写者
Writer(){
while(1){
wait(RP_Write); // 上锁,此时不允许其他的读写操作
WriteFile(); // 写临界区
signal(RP_Write); // 解锁,开放其他的读写操作
}
}
-
写者优先
附加条件:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
伪代码如下:
/* 写者优先 */
//定义信号量
semaphore mutex1 = 1; // 保证每个读者按顺序进入临界区
semaphore mutex2 = 1; // 对R_Count的互斥操作
semaphore mutex3 = 1; // 对W_Count的互斥操作
semaphore WP_Read = 1; // 写者优先,读者和写者互斥
semaphore WP_Write=1; // 如果有读者正在读,写者等待当前读者读完后再写
int R_count=0; // 读者进程数
int W_count=0; // 写者进程数
//写者优先——读者
Reader(){
while(1){
wait(mutex1);//上锁,写者之间互斥
wait(WP_Read);// 上锁,读者写者互斥
wait(mutex2); // 上锁,不允许其他进程操作R_count
R_count++; // 读者数量加1
if(R_count==1) // 如果当前只有一个读者
wait(WP_Write); // 则第一个读者负责上锁,不允许写操作
signal(mutex2); // 解锁,允许其他进程操作R_count
signal(WP_Read);
signal(mutex1);
ReadFile();// 读临界区
wait(mutex2); //上锁,不允许其他进程操作R_count
R_Count--; // 读者数量减1
if(R_Count==0)// 如果当前没有读者
signal(WP_Write); //则可以开始写操作
signal(mutex2); // 解锁,允许其他进程操作R_count
}
}
//写者优先——写者
Writer(){
while(1){
wait(mutex3); // 上锁,不允许其他进程操作R_count
W_count++; //写者数量加1
if(W_count==1){ // 如果当前只有一个写者
wait(WP_Read);// 则第一个读者负责上锁,不允许读操作
}
signal(mutex3);// 解锁,允许其他进程操作R_count
wait(WP_Write);// 上锁,不允许其他的进程写
WriteFile();// 写操作
signal(WP_Write);// 解锁,允许其他的进程写
wait(mutex3); // 上锁,不允许其他进程操作R_count
W_Count--; //写者数量减1
if(W_count==0) // 如果当前没有写进程
signal(WP_Read); // 则可以进行读操作
signal(mutex3); // 解锁,允许其他进程操作R_count
}
}
3.2.2 编程实现
-
定义相关的数据结构和全局数据:
int R_Count = 0;//读者数目 int W_Count = 0;//写者数目 CRITICAL_SECTION RP_Write;//读者优先——写者互斥 CRITICAL_SECTION WP_Write;// 写者优先——写者互斥 CRITICAL_SECTION WP_Read;//写者优先——读者互斥 struct ThreadInfo//线程结构 { int id;//线程序号 char type;//线程类别(判断是读者线程还是写者线程) double delay;//线程延迟 double persist;//线程读写操作持续时间 };
3.2.2.1 读者优先
-
读者优先代码
-
读者线程
读者线程的具体代码实现和注释如下:
void RP_ReaderThread(void* p) { //h_Mutex 保证对R_Count的操作互斥 HANDLE h_Mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex_for_R_Count"); DWORD wait_for_mutex;//等待互斥变量所有权 DWORD m_delay;//延迟时间 DWORD m_persist;//读文件持续时间 int m_id;//线程序号 //从参数中获得读者线程的信息 m_id = ((ThreadInfo*)(p))->id; m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC); m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC); Sleep(m_delay);//延迟等待 printf("【Reader】 thread 【%d】 is 【requesting】...\n", m_id); wait_for_mutex = WaitForSingleObject(h_Mutex, -1);//wait操作,保证对R_Count的操作互斥 R_Count++;//读者数目增加1 if (R_Count == 1) // 如果当前只有一个读者进程 { EnterCriticalSection(&RP_Write); // 则该进程负责上锁,不允许写进程执行 } ReleaseMutex(h_Mutex);// signal操作,释放锁,允许其他进程操作R_count printf("【Reader】 thread 【%d】 begins 【reading】...\n", m_id); Sleep(m_persist); // 模拟读文件延时 printf("【Reader】 thread 【%d】has 【finished】\n", m_id);// 读取完成,线程退出 wait_for_mutex = WaitForSingleObject(h_Mutex, -1);// 上锁,防止多个进程同时操作R_count R_Count--;// 读者数量减1 if (R_Count == 0) // 如果当前读者进程数为0 { LeaveCriticalSection(&RP_Write); // 则最后一个离开的进程负责解锁,此时允许写进程执行 } ReleaseMutex(h_Mutex);// 释放锁,允许其他进程操作R_count }
-
写者线程
写者线程的具体代码实现和注释如下:
/* 读者优先 - 写者线程 p: 写者线程信息 */ // void RP_WriterThread(void* p) { DWORD m_delay;//延迟时间 DWORD m_persist;//写文件持续时间 int m_id;//线程序号 //从参数中获得信息 m_id = ((ThreadInfo*)(p))->id; m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC); m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC); Sleep(m_delay);//延迟等待 printf("【Write】 Thread 【%d】 is 【requesting】....\n", m_id); EnterCriticalSection(&RP_Write);// 上锁,此时不允许其他的读写操作 printf("【Write】 Thread 【%d】begins 【writing】\n", m_id);//写文件(临界区) Sleep(m_persist); //模拟写文件延时 printf("【Write】 Thread 【%d】 has 【finished】\n", m_id);//写操作结束,退出线程 LeaveCriticalSection(&RP_Write);// 解锁,开放其他的读写操作 }
-
读者优先处理函数
void ReaderPriority(char* file) { DWORD n_thread = 0;//线程数目 DWORD thread_ID;//线程ID DWORD wait_for_all;//等待所有线程结束 HANDLE h_Mutex=CreateMutex(NULL, FALSE, "mutex_for_R_Count"); //线程对象的数组 HANDLE h_Thread[MAX_THREAD_NUM]; ThreadInfo thread_info[MAX_THREAD_NUM]; R_Count = 0;//初始化R_Count InitializeCriticalSection(&RP_Write);//初始化临界区 ifstream inFile; inFile.open(file);//打开文件 printf("Reader Priority:\n\n"); while (inFile) { //读入每一个读者、写者的信息 inFile >> thread_info[n_thread].id; inFile >> thread_info[n_thread].type; inFile >> thread_info[n_thread].delay; inFile >> thread_info[n_thread++].persist; inFile.get(); } for (int i = 0; i < (int)(n_thread); i++) { if (thread_info[i].type == READER || thread_info[i].type == 'r') { //创建读者进程 h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(RP_ReaderThread), &thread_info[i], 0, &thread_ID); } else { //创建写者进程 h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(RP_WriterThread), &thread_info[i], 0, &thread_ID); } } //等待所有线程结束 wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1); printf("All done!\n"); }
-
3.2.2.2 写者优先
-
写者优先代码
-
读者线程
void WP_ReaderThread(void* p) { //互斥变量 HANDLE h_mutex1=OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex1"); HANDLE h_mutex2= OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex2"); DWORD wait_for_mutex1;//等待互斥变量所有权 DWORD wait_for_mutex2; DWORD m_delay;//延迟时间 DWORD m_persist;//读文件持续时间 int m_id;//线程序号 //从参数中获得信息 m_id = ((ThreadInfo*)(p))->id; m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC); m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC); Sleep(m_delay);//延迟等待 printf("【Reader】 Thread 【%d】 is 【requesting】....\n", m_id); wait_for_mutex1 = WaitForSingleObject(h_mutex1, -1);//wait操作 EnterCriticalSection(&WP_Read);//wait操作,进入读者临界区 wait_for_mutex2 = WaitForSingleObject(h_mutex2, -1);//P操作,/阻塞互斥对象mutex2,保证对R_Count的访问、修改互斥 R_Count++;//读者数目加1 if (R_Count == 1)// 如果当前只有一个读者 { EnterCriticalSection(&WP_Write); // 则第一个读者负责上锁,不允许写操作 } ReleaseMutex(h_mutex2); // 解锁,允许其他进程操作R_count LeaveCriticalSection(&WP_Read); //让其他读者进入临界区 ReleaseMutex(h_mutex1); printf("【Reader】 Thread 【%d】 begins 【reading】....\n", m_id);//读文件 Sleep(m_persist); // 模拟读文件延时 printf("【Reader】 Thread 【%d】 has 【finished】....\n", m_id); // 读文件结束 wait_for_mutex2 = WaitForSingleObject(h_mutex2, -1);//上锁,不允许其他进程操作R_count R_Count--;// 读者数量减1 if (R_Count == 0) // 如果当前没有读者 { LeaveCriticalSection(&WP_Write);//则可以开始写操作 } ReleaseMutex(h_mutex2);// 解锁,允许其他进程操作R_count }
-
写者线程
void WP_WriterThread(void* p) { DWORD wait_for_mutex3; DWORD m_delay;//延迟时间 DWORD m_persist;//写文件持续时间 int m_id;//线程序号 HANDLE h_mutex3=OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex3"); //从参数中获得信息 m_id = ((ThreadInfo*)(p))->id; m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC); m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC); Sleep(m_delay);//延迟等待 printf("【Writer】 Thread 【%d】 is 【requesting】....\n", m_id); wait_for_mutex3 = WaitForSingleObject(h_mutex3, -1);// 上锁,不允许其他进程操作R_count W_Count++;//写者数量加1 if (W_Count == 1)// 如果当前只有一个写者 { EnterCriticalSection(&WP_Read); // 则第一个读者负责上锁,不允许读操作 } ReleaseMutex(h_mutex3);// 解锁,允许其他进程操作R_count EnterCriticalSection(&WP_Write);// 上锁,不允许其他的进程写 printf("【Writer】 Thread 【%d】 beings 【writing】....\n", m_id);//写临界区 Sleep(m_persist); printf("【Writer】 Thread 【%d】 has 【finished】....\n", m_id); // 写操作结束 LeaveCriticalSection(&WP_Write); //离开临界区 wait_for_mutex3 = WaitForSingleObject(h_mutex3, -1);// 上锁,不允许其他进程操作R_count W_Count--;//写者数量减1 if (W_Count == 0)// 如果当前没有写进程 { LeaveCriticalSection(&WP_Read);// 则可以进行读操作 } ReleaseMutex(h_mutex3); // 解锁,允许其他进程操作R_count }
-
写者优先处理函数
void WriterPriority(char* file) { DWORD n_thread = 0;//线程数目 DWORD thread_ID;//线程ID DWORD wait_for_all;//等待所有线程结束 //互斥对象 HANDLE h_Mutex1; h_Mutex1 = CreateMutex(NULL, FALSE, "mutex1"); HANDLE h_Mutex2; h_Mutex2 = CreateMutex(NULL, FALSE, "mutex2"); HANDLE h_Mutex3; h_Mutex3 = CreateMutex(NULL, FALSE, "mutex3"); //线程对象 HANDLE h_Thread[MAX_THREAD_NUM]; ThreadInfo thread_info[MAX_THREAD_NUM]; R_Count = 0;//初始化R_Count W_Count = 0;//初始化W_Count InitializeCriticalSection(&WP_Write);//初始化临界区 InitializeCriticalSection(&WP_Read); ifstream inFile; inFile.open(file);//打开文件 printf("Reader Priority:\n\n"); while (inFile) { //读入每一个读者、写者的信息 inFile >> thread_info[n_thread].id; inFile >> thread_info[n_thread].type; inFile >> thread_info[n_thread].delay; inFile >> thread_info[n_thread++].persist; inFile.get(); } for (int i = 0; i < (int)(n_thread); i++) { if (thread_info[i].type == READER || thread_info[i].type == 'r') { //创建读者进程 h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(WP_ReaderThread), &thread_info[i], 0, &thread_ID); } else { //创建写者进程 h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(WP_WriterThread), &thread_info[i], 0, &thread_ID); } } //等待所有线程结束 wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1); printf("ALL DONE!\n"); }
-
3.2.3 结果分析
运行环境:本地Windows电脑下的Visual Studio编译器
-
读者优先
由下图的结果可以看出,写者进程 4、2虽然都请求了写操作,但是却是等待所有的读者进程1、3、5读完了再按顺序进行的写入操作,符合读者优先的要求。
-
写者优先
如下图所示,虽然进程3、5都请求了写操作,但是等到写进程4、2、6全部完成写操作之后,最后才能完成线程3,5的读进程。满足了写者优先的要求。
3.3 哲学家就餐
3.3.1 问题描述
-
问题背景
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
-
避免死锁的方法
- 可以对哲学家进程施加一些限制条件,比如最多允许四个哲学家同时进餐。这样可以保证至少有一个哲学家是可以拿到左右两只筷子的
- 要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家刚好相反。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭,那么只会有其中一个可以拿起第一只筷子,另一个会直接阻塞。这就
避免了占有一支后再等待另一只的情况。 - 仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子。
3.3.2 编程实现
3.3.2.1 解法1
哲学家进程分别拿起左边的筷子和右边的筷子,拿起每只筷子前都会对该筷子上锁,当拿起一双筷子后,哲学家就开始吃饭。吃完饭后,会释放筷子资源。但是该算法存在的问题是,有可能每个哲学家都拿起自己右边的筷子,导致所有的哲学家都不能吃饭,产生死锁现象。
关键代码如下:
int chopsticks[5]; // 访问5只筷子的互斥信号量
void philosopher(int i) {
while (true) {
think(i);// 哲学家处于思考状态
Wait(chopsticks[i]);// 哲学家拿起左边的筷子
printf("哲学家 %d 拿起左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]); // 哲学家拿起右边的筷子
printf("哲学家 %d 拿起右边的筷子\n", i);
eat(i); // 得到一双筷子后,哲学家开始吃饭
Signal(chopsticks[i]);// 哲学家放下左边的筷子
printf("哲学家 %d 放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]); // 哲学家放下右边的筷子
printf("哲学家 %d 放下右边的筷子\n", i);
}
}
3.3.2.2 解法2
思路:至多只允许四位哲学家同时去拿左筷子,最终能保证至少有一位哲学家能进餐,并在用完后释放两只筷子供他人使用。因此添加互斥信号量count=4, 表示最多只允许4个哲学家用餐。
关键代码如下:
int count;// 最多只允许4个哲学家进餐,count = IniSem(4);
int chopsticks[5]; // 设置5只筷子的互斥信号量
void philosopher(int i) {
while (true) {
think(i);
Wait(count);
printf("哲学家 %d 加入\n", i);
Wait(chopsticks[i]);
printf("哲学家 %d 拿起左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家 %d 拿起右边的筷子\n", i);
eat(i);
Signal(chopsticks[i]);
printf("哲学家 %d 放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家 %d 放下右边的筷子\n", i);
Signal(count);
printf("哲学家 %d 离开\n", i);
}
}
3.3.2.2 解法3
思路:规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子;而偶数号的哲学家则先拿起他右边的筷子,然后再去拿他左边的筷子。按此规定,将是1、2号哲学家竞争1号筷子,3、4号哲学家竞争3号筷子。即五个哲学家都竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一个哲学家能获得两支筷子而进餐
核心代码如下:
int chopsticks[5]; // 设置访问5只筷子的互斥信号量
void philosopher(int i) {
while (true) {
think(i);// 哲学家思考
if (i % 2 == 1) { // 如果是奇数,则先拿左边的筷子
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿右边的筷子\n", i);
eat(i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
} else {// 如果是偶数,则先拿右边的筷子
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿右边的筷子\n", i);
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n", i);
eat(i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
}
}
}
3.3.2.2 解法4
仅当哲学家的左,右两只筷子均可用时,才允许他拿起筷子进餐。通过互斥信号量 mutex 对哲学家进餐之前取左侧和右侧筷子的操作进行保护,可以防止死锁的出现。(当第i个哲学家将左右筷子都拿到了才允许其他哲学家拿筷子)如:哲学家4在拿起左筷子与右筷子中间没有其他的哲学家拿任何一个筷子,虽然他们也饿了,虽然他们要拿的筷子可能并没有被占用,所以这个会造成资源(筷子)得不到有效的利用。
int chopsticks[5];// 访问5只筷子的互斥信号量
int takeMutex;// 保证一个哲学家可以同时拿两个筷子
void philosopher(int i) {
while (true) {
think(i);
Wait(takeMutex); // 上锁,保证哲学家可以同时拿到左右两边的筷子
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿有右边的筷子\n\n", i);
Signal(takeMutex); // 释放锁
eat(i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
}
}
3.3.3 结果分析
运行环境:64位的Utuntu虚拟机
-
输入
make
-
输入
make basic
、./basic.out
模拟过程如下:
- 输入
make most4
、./most4.out
输出结果如下图,最多只能有4个哲学家加入拿筷子的队伍。
- 输入
make oddleft
、./oddleft.out
输出结果如下图,可以看到奇数的哲学家总是先拿起左边的筷子,而偶数的哲学家总是先拿起右边的筷子。
- 输入
make get2
、./get2.out
输出的结果如下图,当哲学家在拿筷子的过程中,需要把两个筷子都拿到手,在此期间不会有其他的哲学家再做拿筷子的动作,从而避免了死锁。
4. 心得体会
通过这次实验,更加深入的理解了操作系统的进程同步问题,尤其是一些经典的进程同步问题。由于系统中存在多个进程和有限的资源,就会出现同步问题。如果一个资源同时在多个进程之间共享,则可能导致数据不一致。同时还学习了操作系统中同步的需要、信号量的使用和临界区。最后,通过编写代码分别在Windows和Linux系统下实现了多线程的编程模型,模拟实现了操作系统的进程同步,非常有意思。
5. 源码附录
5.1 Product_Consumer.cpp
#include<iostream>
#include<mutex>
#include<chrono>
#include<thread>
using namespace std;
int n = 10; // 单个缓存区大小
int in[4] = { 0,0,0,0 }, out[4] = { 0,0,0,0 }; // 生产指针,消费指针
int count[3] = { 0,0,0 }; // 分别对应三个缓冲区所存放的数据个数,初始时都为0
int buffer[3][10]; // 3个缓存区(分别是1,2,3)每个缓冲区可以存放10个数据
mutex mtx[3]; // 缓冲区互斥量
mutex display_mtx; // 输出数据锁
/*
* 生产者函数
*/
void producer(int id)
{
while(1){
for (int i = 0; i < 3; i++) // 依次遍历三个缓冲区,看哪一个缓冲区满足写条件
{
if (mtx[i].try_lock()) // 如果可以对第i个缓冲区上锁
{
if (count[i]!=n) // 且第i个缓冲区不满
{
buffer[i][in[i]] = 1; // 第i个缓冲区写指针对应的位置写为1
display_mtx.lock(); // 输出前上锁
cout << "【Producer" << id << "】: is producing 【product" << in[i] <<"】at 【Buffer"<<i+1 <<"】"<< endl;
cout << "【Buffer"<<i+1 << "】"<<":";
for (int j = 0; j < n; j++)
{
cout << buffer[i][j] << " "; // 遍历输出第i个缓冲区的值
}
cout<<endl<<"---------------------------------------------------------------------"<<endl;
display_mtx.unlock();// 输出后解锁
in[i] = (in[i] + 1) % n; // 第i个缓冲区的写指针循环遍历
count[i]++; // 第i 个缓冲区的个数加1
mtx[i].unlock(); // 对第i个缓冲区解锁
this_thread::sleep_for(chrono::seconds(1)); // 延迟1s
break;
}
else // 第i个缓冲区已满
{
mtx[i].unlock(); // 对第i个缓冲区解锁
}
}
}
}
}
/**
* 消费者函数
*/
void consumer(int id)
{
while(1){
for (int i = 0; i < 3; i++) // 依次遍历三个缓冲区,看哪一个缓冲区满足读条件
{
if (mtx[i].try_lock()) // 试探第i个缓冲区,并上锁
{
if (count[i] != 0) // 如果第i个缓冲区不为空
{
buffer[i][out[i]] = 0; // 将都指针指向的位置写入0,即消费
display_mtx.lock(); // 写数据上锁
cout << "【Consumer" << id << "】: is consuming 【product" << out[i] <<"】from 【Buffer"<<i+1 <<"】"<< endl;
cout << "【Buffer"<<i+1 << "】" << ":";
for (int j = 0; j < n; j++)
{
cout << buffer[i][j] << " "; // 输出第i个缓冲区的值
}
cout<<endl<<"---------------------------------------------------------------------"<<endl;
display_mtx.unlock(); // 写数据解锁
out[i] = (out[i] + 1) % n; // 读指针后移
count[i]--;// 第i个缓冲区的个数减1
mtx[i].unlock(); // 缓冲区解锁
this_thread::sleep_for(chrono::seconds(5)); // 等待5s
break;
}
else // 第i个缓冲区为空,则解锁
{
mtx[i].unlock();
}
}
}
} ;
}
int main() {
//定义生产者和消费者
thread t1(producer, 1);
thread t2(consumer, 1);
thread t3(producer, 2);
thread t4(consumer, 2);
thread t5(producer, 3);
thread t6(consumer, 3);
thread t7(producer, 4);
thread t8(consumer, 4);
//线程开始工作
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
return 0;
}
5.2 Read_Write.cpp
#include "windows.h"
#include <conio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <io.h>
#include <string.h>
#include <stdio.h>
using namespace std;
#define READER 'R' //读者
#define WRITER 'W' //写者
#define INTE_PER_SEC 1000 //每秒时钟中断数目
#define MAX_THREAD_NUM 64//最大线程数目
#define MAX_FILE_NUM 32//最大数据文件数目
#define MAX_STR_LEN 32//字符串长度
int R_Count = 0;//读者数目
int W_Count = 0;//写者数目
CRITICAL_SECTION RP_Write;//读者优先——写者互斥
CRITICAL_SECTION WP_Write;// 写者优先——写者互斥
CRITICAL_SECTION WP_Read;//写者优先——读者互斥
struct ThreadInfo//线程结构
{
int id;//线程序号
char type;//线程类别(判断是读者线程还是写者线程)
double delay;//线程延迟
double persist;//线程读写操作持续时间
};
/*
*
读者优先-读者线程
p:读者线程信息
*
*/
void RP_ReaderThread(void* p)
{
//h_Mutex 保证对R_Count的操作互斥
HANDLE h_Mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex_for_R_Count");
DWORD wait_for_mutex;//等待互斥变量所有权
DWORD m_delay;//延迟时间
DWORD m_persist;//读文件持续时间
int m_id;//线程序号
//从参数中获得读者线程的信息
m_id = ((ThreadInfo*)(p))->id;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC);
m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC);
Sleep(m_delay);//延迟等待
printf("【Reader】 thread 【%d】 is 【requesting】...\n", m_id);
wait_for_mutex = WaitForSingleObject(h_Mutex, -1);//wait操作,保证对R_Count的操作互斥
R_Count++;//读者数目增加1
if (R_Count == 1) // 如果当前只有一个读者进程
{
EnterCriticalSection(&RP_Write); // 则该进程负责上锁,不允许写进程执行
}
ReleaseMutex(h_Mutex);// signal操作,释放锁,允许其他进程操作R_count
printf("【Reader】 thread 【%d】 begins 【reading】...\n", m_id);
Sleep(m_persist); // 模拟读文件延时
printf("【Reader】 thread 【%d】has 【finished】\n", m_id);// 读取完成,线程退出
wait_for_mutex = WaitForSingleObject(h_Mutex, -1);// 上锁,防止多个进程同时操作R_count
R_Count--;// 读者数量减1
if (R_Count == 0) // 如果当前读者进程数为0
{
LeaveCriticalSection(&RP_Write); // 则最后一个离开的进程负责解锁,此时允许写进程执行
}
ReleaseMutex(h_Mutex);// 释放锁,允许其他进程操作R_count
}
/*
读者优先 - 写者线程
p: 写者线程信息
*/
//
void RP_WriterThread(void* p)
{
DWORD m_delay;//延迟时间
DWORD m_persist;//写文件持续时间
int m_id;//线程序号
//从参数中获得信息
m_id = ((ThreadInfo*)(p))->id;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC);
m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC);
Sleep(m_delay);//延迟等待
printf("【Write】 Thread 【%d】 is 【requesting】....\n", m_id);
EnterCriticalSection(&RP_Write);// 上锁,此时不允许其他的读写操作
printf("【Write】 Thread 【%d】begins 【writing】\n", m_id);//写文件(临界区)
Sleep(m_persist); //模拟写文件延时
printf("【Write】 Thread 【%d】 has 【finished】\n", m_id);//写操作结束,退出线程
LeaveCriticalSection(&RP_Write);// 解锁,开放其他的读写操作
}
/*
* 读者优先处理函数
* file:文件名
*/
void ReaderPriority(char* file)
{
DWORD n_thread = 0;//线程数目
DWORD thread_ID;//线程ID
DWORD wait_for_all;//等待所有线程结束
HANDLE h_Mutex=CreateMutex(NULL, FALSE, "mutex_for_R_Count");
//线程对象的数组
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
R_Count = 0;//初始化R_Count
InitializeCriticalSection(&RP_Write);//初始化临界区
ifstream inFile;
inFile.open(file);//打开文件
printf("Reader Priority:\n\n");
while (inFile)
{
//读入每一个读者、写者的信息
inFile >> thread_info[n_thread].id;
inFile >> thread_info[n_thread].type;
inFile >> thread_info[n_thread].delay;
inFile >> thread_info[n_thread++].persist;
inFile.get();
}
for (int i = 0; i < (int)(n_thread); i++)
{
if (thread_info[i].type == READER || thread_info[i].type == 'r')
{
//创建读者进程
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(RP_ReaderThread),
&thread_info[i],
0, &thread_ID);
}
else
{
//创建写者进程
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(RP_WriterThread),
&thread_info[i],
0, &thread_ID);
}
}
//等待所有线程结束
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1);
printf("All done!\n");
}
/*
写者优先--读者进程
p:读者线程信息
*/
void WP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_mutex1=OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex1");
HANDLE h_mutex2= OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex2");
DWORD wait_for_mutex1;//等待互斥变量所有权
DWORD wait_for_mutex2;
DWORD m_delay;//延迟时间
DWORD m_persist;//读文件持续时间
int m_id;//线程序号
//从参数中获得信息
m_id = ((ThreadInfo*)(p))->id;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC);
m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC);
Sleep(m_delay);//延迟等待
printf("【Reader】 Thread 【%d】 is 【requesting】....\n", m_id);
wait_for_mutex1 = WaitForSingleObject(h_mutex1, -1);//wait操作
EnterCriticalSection(&WP_Read);//wait操作,进入读者临界区
wait_for_mutex2 = WaitForSingleObject(h_mutex2, -1);//P操作,/阻塞互斥对象mutex2,保证对R_Count的访问、修改互斥
R_Count++;//读者数目加1
if (R_Count == 1)// 如果当前只有一个读者
{
EnterCriticalSection(&WP_Write); // 则第一个读者负责上锁,不允许写操作
}
ReleaseMutex(h_mutex2); // 解锁,允许其他进程操作R_count
LeaveCriticalSection(&WP_Read); //让其他读者进入临界区
ReleaseMutex(h_mutex1);
printf("【Reader】 Thread 【%d】 begins 【reading】....\n", m_id);//读文件
Sleep(m_persist); // 模拟读文件延时
printf("【Reader】 Thread 【%d】 has 【finished】....\n", m_id); // 读文件结束
wait_for_mutex2 = WaitForSingleObject(h_mutex2, -1);//上锁,不允许其他进程操作R_count
R_Count--;// 读者数量减1
if (R_Count == 0) // 如果当前没有读者
{
LeaveCriticalSection(&WP_Write);//则可以开始写操作
}
ReleaseMutex(h_mutex2);// 解锁,允许其他进程操作R_count
}
/*
写者优先--写者线程
p:写者线程信息
*/
void WP_WriterThread(void* p)
{
DWORD wait_for_mutex3;
DWORD m_delay;//延迟时间
DWORD m_persist;//写文件持续时间
int m_id;//线程序号
HANDLE h_mutex3=OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex3");
//从参数中获得信息
m_id = ((ThreadInfo*)(p))->id;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC);
m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC);
Sleep(m_delay);//延迟等待
printf("【Writer】 Thread 【%d】 is 【requesting】....\n", m_id);
wait_for_mutex3 = WaitForSingleObject(h_mutex3, -1);// 上锁,不允许其他进程操作R_count
W_Count++;//写者数量加1
if (W_Count == 1)// 如果当前只有一个写者
{
EnterCriticalSection(&WP_Read); // 则第一个读者负责上锁,不允许读操作
}
ReleaseMutex(h_mutex3);// 解锁,允许其他进程操作R_count
EnterCriticalSection(&WP_Write);// 上锁,不允许其他的进程写
printf("【Writer】 Thread 【%d】 beings 【writing】....\n", m_id);//写临界区
Sleep(m_persist);
printf("【Writer】 Thread 【%d】 has 【finished】....\n", m_id); // 写操作结束
LeaveCriticalSection(&WP_Write); //离开临界区
wait_for_mutex3 = WaitForSingleObject(h_mutex3, -1);// 上锁,不允许其他进程操作R_count
W_Count--;//写者数量减1
if (W_Count == 0)// 如果当前没有写进程
{
LeaveCriticalSection(&WP_Read);// 则可以进行读操作
}
ReleaseMutex(h_mutex3); // 解锁,允许其他进程操作R_count
}
/*
写者优先处理函数
file:文件名
*/
void WriterPriority(char* file)
{
DWORD n_thread = 0;//线程数目
DWORD thread_ID;//线程ID
DWORD wait_for_all;//等待所有线程结束
//互斥对象
HANDLE h_Mutex1;
h_Mutex1 = CreateMutex(NULL, FALSE, "mutex1");
HANDLE h_Mutex2;
h_Mutex2 = CreateMutex(NULL, FALSE, "mutex2");
HANDLE h_Mutex3;
h_Mutex3 = CreateMutex(NULL, FALSE, "mutex3");
//线程对象
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
R_Count = 0;//初始化R_Count
W_Count = 0;//初始化W_Count
InitializeCriticalSection(&WP_Write);//初始化临界区
InitializeCriticalSection(&WP_Read);
ifstream inFile;
inFile.open(file);//打开文件
printf("Writer Priority:\n\n");
while (inFile)
{
//读入每一个读者、写者的信息
inFile >> thread_info[n_thread].id;
inFile >> thread_info[n_thread].type;
inFile >> thread_info[n_thread].delay;
inFile >> thread_info[n_thread++].persist;
inFile.get();
}
for (int i = 0; i < (int)(n_thread); i++)
{
if (thread_info[i].type == READER || thread_info[i].type == 'r')
{
//创建读者进程
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(WP_ReaderThread),
&thread_info[i],
0, &thread_ID);
}
else {
//创建写者进程
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(WP_WriterThread),
&thread_info[i],
0, &thread_ID);
}
}
//等待所有线程结束
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1);
printf("ALL DONE!\n");
}
int main(int argc, char* argv[])
{
printf("----------------------Lab6: 读者写者问题--------------------------\n");
printf("Name: ZYW | Date: 2022/3/29\n");
printf("------------------------------------------------------------------\n");
char ch;
char str[50] = ".\\thread.dat";//最好输入数据的绝对路径;
while (1)
{
//打印提示信息
printf("---------------MENU---------------\n");
printf(" R:Reader Priority\n");
printf(" W:Writer Priority\n");
printf(" E:Exit \n");
printf("----------------------------------\n");
printf("Please Enter R or W or E:");
//如果信息不正确,继续输入
ch = (char)_getch();
while (ch != 'R' && ch != 'W' && ch != 'E')
{
ch = (char)_getch();
}
system("cls");
//选择E,返回
if (ch == 'E')
return 0;
//选择R,读者优先
else if (ch == 'R')
ReaderPriority(str);
//选择W,写者优先
else if(ch=='W')
WriterPriority(str);
//结束
printf("\n Please Enter any Keys to Exit:");
_getch();
system("cls");
}
return 0;
}
5.3 Solution1_basic.cpp
/*
basic.cpp
最基本的解决方案,但是可能导致死锁
*/
#include "semaphore.h"
#include "philosopher.h"
#include <unistd.h>
#include <sys/types.h>
void philosopher(int i); // 哲学家进程
int chopsticks[5]; // 访问5只筷子的互斥信号量
int main(int argc, char * args[]) {
for (int i = 0; i < 5; ++i)
chopsticks[i] = IniSem(1); // 初始化互斥信号量
int fpid = 1;
int amount = 5;
while (fpid != 0 && --amount) // 创建5个进程
fpid = fork();
philosopher(amount);
return 0;
}
void philosopher(int i) {
while (true) {
think(i);// 哲学家处于思考状态
Wait(chopsticks[i]);// 哲学家拿起左边的筷子
printf("哲学家 %d 拿起左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]); // 哲学家拿起右边的筷子
printf("哲学家 %d 拿起右边的筷子\n", i);
eat(i); // 得到一双筷子后,哲学家开始吃饭
Signal(chopsticks[i]);// 哲学家放下左边的筷子
printf("哲学家 %d 放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]); // 哲学家放下右边的筷子
printf("哲学家 %d 放下右边的筷子\n", i);
}
}
5.3 Solution2_most4.cpp
#include "semaphore.h"
#include "philosopher.h"
#include <unistd.h>
#include <sys/types.h>
void philosopher(int i); // 哲学家进程
int count;// 最多只允许4个哲学家进餐
int chopsticks[5]; // 设置5只筷子的互斥信号量
int main(int argc, char * args[]) {
for (int i = 0; i < 5; ++i)
chopsticks[i] = IniSem(1); // 初始化5只筷子的互斥信号量
count = IniSem(4); // 最多只允许4个哲学家用餐
int fpid = 1;
int amount = 5;
while (fpid != 0 && --amount)
fpid = fork();// 创建5个进程
philosopher(amount);
return 0;
}
void philosopher(int i) {
while (true) {
think(i);
Wait(count);
printf("哲学家 %d 加入\n", i);
Wait(chopsticks[i]);
printf("哲学家 %d 拿起左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家 %d 拿起右边的筷子\n", i);
eat(i);
Signal(chopsticks[i]);
printf("哲学家 %d 放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家 %d 放下右边的筷子\n", i);
Signal(count);
printf("哲学家 %d 离开\n", i);
}
}
5.4 Solution3_oddleft.cpp
#include "semaphore.h"
#include "philosopher.h"
#include <unistd.h>
#include <sys/types.h>
void philosopher(int i);
int chopsticks[5]; // 设置访问5只筷子的互斥信号量
int main(int argc, char * args[]) {
for (int i = 0; i < 5; ++i)
chopsticks[i] = IniSem(1);// 初始化5只筷子的互斥信号量
int fpid = 1;
int amount = 5;
while (fpid != 0 && --amount) // 创建5个互斥进程
fpid = fork();
philosopher(amount);
return 0;
}
void philosopher(int i) {
while (true) {
think(i);// 哲学家思考
if (i % 2 == 1) { // 如果是奇数,则先拿左边的筷子
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿右边的筷子\n", i);
eat(i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
} else {// 如果是偶数,则先拿右边的筷子
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿右边的筷子\n", i);
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n", i);
eat(i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
}
}
}
5.5 Solution4_get2.cpp
#include "semaphore.h"
#include "philosopher.h"
#include <unistd.h>
#include <sys/types.h>
void philosopher(int i);
int chopsticks[5];// 访问5只筷子的互斥信号量
int takeMutex;// 保证一个哲学家可以同时拿两个筷子
int main(int argc, char * args[]) {
for (int i = 0; i < 5; ++i)
chopsticks[i] = IniSem(1);
takeMutex = IniSem(1);
int fpid = 1;
int amount = 5;
while (fpid != 0 && --amount)
fpid = fork(); // 创建5个哲学家进程
philosopher(amount);
return 0;
}
void philosopher(int i) {
while (true) {
think(i);
Wait(takeMutex); // 上锁,保证哲学家可以同时拿到左右两边的筷子
Wait(chopsticks[i]);
printf("哲学家%d拿左边的筷子\n\n", i);
Wait(chopsticks[(i + 1) % 5]);
printf("哲学家%d拿有右边的筷子\n\n", i);
Signal(takeMutex); // 释放锁
eat(i);
Signal(chopsticks[i]);
printf("哲学家%d放下左边的筷子\n", i);
Signal(chopsticks[(i + 1) % 5]);
printf("哲学家%d放下右边的筷子\n", i);
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)