Linux操作系统--读者写者问题(读者优先)
程序员文章站
2022-07-04 23:44:14
...
为什么要进行并发控制
在并发程序中,多个进程或者线程的运行,本来是可以毫无关系的,也就是完全的异步执行,但是现实中往往异步执行不能满足需求
- 假如只有一个苹果,A和B都要执行吃苹果的程序,如果只是异步执行的话,他们都会认为自己吃完了苹果,但是无法确定到底谁吃到了苹果
引出的问题就是并发执行的程序要想对共同的资源进行访问,需要实现同步,才可以保证对于共同资源的有序使用,避免出现问题。
P/V信号量编程
- 临界资源:并发程序需要访问的相同的资源
- P操作是指,检测临界区域能否使用,等待资源
- V操作是指,释放临界资源,通知等待的进程
实现读者写者的并发控制(读者优先)
-
问题分析
对于同样的资源,读的时候不能写,写的时候不能读,也就是读写互斥,用信号量可以完美实现 -
实现思路
- 读者线程
- 读者可以并发的读临界资源,因此,可以不对读操作加互斥量
- 读者优先的特性应该在读者线程中体现出来,即只有读者全部读完之后,才会释放临界资源
- 为了保证所有的读者都读完再进行临界资源的释放,需要统计读者的数量,每个读线程为readCount加1,读完之后再减1
-
关键
对于readCount加1减1的操作,如果不进行同步,多个读线程共同操作,会出现同步问题,因此,需要对readCount的操作加互斥量,保证只有一个读者对这一数字进行改变
- 写者线程
写者只能单独写临界资源,因此,需要对写操作加互斥量
- 读者线程
-
待解决疑问
如果一个写线程在使用资源,一个读线程和一个写线程都在等待资源,如何保证写线程写完释放资源之后,可以将资源分配给读线程,保证读者优先 -
代码分析
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <fstream>
#include <iostream>
using namespace std;
#define sleep(n) Sleep(n*1000)
struct ThreadInfo
{
int tid; //线程ID
char role; //扮演角色 R or W
double delay; //线程延迟
double persist; //线程读写操作持续时间
};
int ReadCount = 0; //读者数量
HANDLE Rmutex; //信号量
CRITICAL_SECTION RW_mutex; //临界区
void ReaderThread(void * p)
{
//创建线程会传入相关参数
//DWORD全称Double Word,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。
DWORD m_delay = ((ThreadInfo *)(p))->delay;
DWORD m_persist = ((ThreadInfo *)(p))->persist;
int m_id = ((ThreadInfo*)(p))->tid;
//模拟读者想要读之前的等待时间
sleep(m_delay);
printf("Reader thread %d sents the reading require ! \n", m_id);
//获取操作readCount的权限,对readCount加1
DWORD wait_for_mutex = WaitForSingleObject(Rmutex, -1);
//获取临界资源
if (ReadCount == 0)
{
//等待临界区,直到进入临界区
EnterCriticalSection(&RW_mutex);
//DWORD waits = WaitForSingleObject(RW_mutex, -1);
}
//统计有几个读者
ReadCount++;
//通知其他读者
ReleaseMutex(Rmutex);
// 模拟读者进行阅读
printf("Reader thread %d begins to read file ! \n", m_id);
sleep(m_persist);
printf("Reader thread %d finished reading file ! \n", m_id);
wait_for_mutex = WaitForSingleObject(Rmutex, -1);
ReadCount--;
//没有读者才释放临界区,也是为什么前边需要统计读者数量的原因,
//因为要让所有的读者都读完,才释放临界区
if (ReadCount == 0)
{
//释放临界区
LeaveCriticalSection(&RW_mutex); //ReleaseMutex(RW_mutex);
}
//释放互斥信号量,通知读者来读
ReleaseMutex(Rmutex);
}
void WriterThread(void * p)
{
DWORD m_delay;
DWORD m_persist;
int m_id;
//从参数中获取信息
m_delay = ((ThreadInfo *)(p))->delay;
m_persist = ((ThreadInfo *)(p))->persist;
m_id = ((ThreadInfo*)(p))->tid;
sleep(m_delay); //延迟等待
printf("Writer thread %d sents the writing require ! \n", m_id);
//等待进入临界区,直到进入,P操作
EnterCriticalSection(&RW_mutex); //DWORD waits = WaitForSingleObject(RW_mutex, -1);
//执行写操作
printf("Writer thread %d begins to write file ! \n", m_id);
sleep(m_persist);
printf("Writer thread %d finished writing file ! \n", m_id);
//释放资源,V操作
LeaveCriticalSection(&RW_mutex);//ReleaseMutex(RW_mutex);
}
int main(int argc, char ** argv)
{
DWORD n_thread = 0; //线程数目
DWORD thread_ID; //线程ID
//线程对象数组
HANDLE h_thread[20];
ThreadInfo thread_info[20];
//初始化同步对象
Rmutex = CreateMutex(NULL, FALSE, LPCWSTR("mutex_for_readcount"));
InitializeCriticalSection(&RW_mutex); //初始化临界区 //RW_mutex = OpenMutex(MUTEX_ALL_ACCESS, false, LPCWSTR("mutex_for"));
//读取输入文件
ifstream inFile;
inFile.open("rw.txt");
if (!inFile)
{
printf("error in open file !\n");
return -1;
}
while (inFile)
{
inFile >> thread_info[n_thread].tid;
inFile >> thread_info[n_thread].role;
inFile >> thread_info[n_thread].delay;
inFile >> thread_info[n_thread].persist;
inFile.get();
n_thread++;
}
//创建线程
for (int i = 0; i < n_thread; i++)
{
if (thread_info[i].role == 'R' || thread_info[i].role == 'r')
{
//根据上述方法创建读者线程
h_thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(ReaderThread), &thread_info[i], 0, &thread_ID);
}
else
{
//根据上述方法创建写者线程
h_thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(WriterThread), &thread_info[i], 0, &thread_ID);
}
}
//等待所有线程结束,直到结束
DWORD wait_for_all = WaitForMultipleObjects(n_thread, h_thread, true, -1);
printf("All reader and writer have finished operating !\n");
_getch();
return 0;
}
上一篇: 微信小程序实现手势图案锁屏功能
下一篇: Centos上安装MySQL