Operating system principle experiment - process synchronization

Operating system principle experiment report

 Experiment topic Experiment   2 process synchronization   

Experiment 2, process synchronization

1.1 Purpose of the experiment

  1. The core of modern operating system is multiprogramming, multiprocessor and distributed processor. The basis of these schemes and operating system design technology is concurrency. Conflict and cooperation problems arise when multiple processes execute concurrently, whether in the case of a multiprocessor system or in a single-processor multiprogramming system.
  2. Understand the use of mutual exclusion and synchronization to solve problems in the operating system.
  3. Use the semaphore mechanism to realize the reader-writer problem of reader priority and writer priority respectively, so as to grasp the basic concepts of mutual exclusion and synchronization.

1.2 Experimental content and requirements

  1. In the Windows environment, create a console process that contains n threads. Use these n threads to represent n readers or writers. Each thread performs read and write operations according to the requirements of the corresponding test data file (described later). The semaphore mechanism is used to realize the reader-writer problem of reader priority and writer priority respectively.
  2. Read and write operation restrictions for the reader-writer problem (including reader-first and writer-first):
  3. 1) Write-write mutual exclusion, that is, no two writers can write at the same time.
  4. 2) Read-write mutual exclusion, that is, one thread cannot be reading while another thread is writing at the same time.
  5. 3) Read-read allowed, that is, there can be one or more readers reading.
  6. Additional restriction on reader priority: If a reader requests a read operation while another reader is already reading, the reader can start the read operation directly.
  7. Additional restrictions on writer priority: If a reader requests a read operation while another writer is waiting to access the shared resource, the reader must wait until no writers are waiting before starting the read operation.
  8. Running result display requirements: It is required to display a line of prompt information when each thread is created, a read-write operation application is issued, a read-write operation is started, and a result read-write operation is performed, so as to ensure that all processes comply with the corresponding read and write operation restrictions.
  9. The test data file includes n lines of test data, respectively describing whether the created n threads are readers or writers, and the start time and duration of read and write operations. Each line of test data includes four fields, each field is separated by a space. The first field is a positive integer, indicating the line program number. The second field indicates the corresponding thread role, R indicates a reader, and W indicates a writer. The third field is a positive number, indicating the start time of the read and write operations: after the thread is created, the request for reading and writing to the shared resource is issued after a corresponding delay (in seconds). The fourth field is a positive number, indicating the duration of read and write operations. When the thread reads and writes the application successfully, it starts to read and write the shared resource. The operation lasts for a corresponding period of time and ends, and the shared resource is released.

Here is an example of a test data file:

1       R       3       5

                                                               2       W       4       5

3       R       5       2

4       R       6       5

                                                               5       W       5.1     3

1.3 Experimental techniques

  1. Before starting this lab, please review the relevant content of the textbook
  2. The following preparations are required:

1. A computer running Windows operating system

2. Visual C Professional Edition or Enterprise Edition needs to be installed in the computer

1.4 Experimental steps

(1) In the Windows environment, create a console process that contains n threads. use

These n threads represent n readers or writers. Each thread presses the corresponding test data file (followed by

Introduction) for read and write operations. Use the semaphore mechanism to implement reader-first and writer-first respectively

(2) Reader-writer problem. Read and write operation restrictions for the reader-writer problem (including reader-first and writer-first):

(3) Use the test data file to run and analyze the experimental results

The experimental code flow chart is as follows:

 

1.5 Experimental results and analysis instructions

Compile the sample code and jump out of the selection interface, as shown in the figure below:

 

Here is an example of a test data file:

1      R        3       5

2      W        4       5

3      R        5       2

4      R        6       5

5      W        5.1     3

Write the content to a txt file, and modify the file of the sample code to the location of the txt text.

 

Choose to read files first, that is, enter 1, and the running results are as follows:

 

process:

① Process 1 sends a read request and enters the read file.

②Process 2 sends a write request, and read and write are mutually exclusive, so the write operation cannot be performed.

③ Process 3 sends a read request and enters the read file.

④ Process 5 sends a write request, and read and write are mutually exclusive, so the write operation cannot be performed.

⑤ Process 4 sends a read request and enters the read file.

⑥ Processes 3, 1, and 4 complete the read operation successively.

⑦ Process 2 starts the write operation and ends.

⑧Process 5 starts the write operation and ends

Select write priority, that is, enter 2, and the running results are as follows:

 

process:

① Process 1 sends a write request and enters the write file.

② Process 2 sends a write request and enters the write file.

③ Processes 3, 5, and 4 send write requests.

④ Process 1 ends, process 3 write operation starts.

⑤ Process 2 ends, and process 5 write operation begins.

⑥ Process 3 ends, and process 4 write operation starts.

⑦Processes 5 and 4 end.

Analysis of experimental results:

Store all readers and all writers in a reader waiting queue and a writer waiting queue respectively. Whenever reading is allowed, one or more reader threads are released from the reader queue for reading operations; whenever writing is allowed, Just release a writer from the writer queue for writing.

Reader First Situations:

    Reader priority means that readers do not need to wait unless a writer is writing to the file. So you can use an integer variable

The quantity read-count records the current number of readers, which is used to determine whether to release the waiting writer thread (when

When read-count=0 , it indicates that all readers have finished reading, and it is necessary to release a writer in the writer waiting queue). each read

When the reader starts to read the file, the read-count variable must be modified. Therefore, a mutual exclusion object mutex is needed to realize mutual exclusion when modifying the global variable read-count .

In addition, in order to realize write - write mutual exclusion, it is necessary to add a critical section object write . When a writer issues a write request, it must claim ownership of the critical section object. In this way, read - write mutual exclusion can also be achieved, when read -count=1

(that is, when the first reader arrives), the reader thread must also claim ownership of the critical section object. While the reader has ownership of the critical section, the writer blocks on the critical section object write . When the writer has the ownership of the critical section, the first reader blocks on write after judging " read-count==1 " , and the rest of the readers are blocked on mutex because they are waiting for the judgment on read-count .

Writer-first case:

Writer-first is similar to reader-first. The difference is that once a writer arrives, it should write to the file as soon as possible, and if there is a writer waiting, the newly arrived reader is not allowed to read. To this end an entire

The type variable write-count is used to record the number of writers who are waiting. When write-count=0 , the queue of waiting reader threads can be released. In order to achieve mutual exclusion for the global variable write-count , a mutex object mutex3 must be added .

In order to achieve writer priority, a critical section object read should be added . When a writer is writing a file or waiting, the read

Or must block on read . In addition to implementing mutual exclusion on the operation of the global variable read-count , the reader thread must also have a mutex object to implement mutual exclusion on the blocking read process. These two mutex objects are named mutex1 and mutex2 respectively .

  

1.6 Experiment experience ( problems and solutions encountered in the experiment )

1) Experiment reflection

The sample code is very long, it is difficult to read, and the code logic is rigorous, which further understands process synchronization and mutual exclusion.

2) Experimental Harvest

1. All readers and all writers can be stored in a reader waiting queue and a writer waiting queue respectively; whenever reading is allowed, one or more reader threads are released from the reader queue for reading operations; whenever writing When allowed, a writer is released from the writer queue for writing.

2. Understand the functions of some APIs involved in process synchronization:

①CreateThread function: create a thread.

②CreateMutex function: Generate a named mutex object.

③CreateSemaphore function: Create a named or anonymous mutex object.

④WaitForSingleObject function: Make the program wait until the semaphore appears or exceeds the maximum waiting time, and the semaphore is greater than or equal to 1.

⑤ReleaseSemaphore function: add a quantity of the specified size to the semaphore pointed to, and return a non-zero value if the execution is successful.

⑥ReleaseMutex function: used to open the mutex, that is, add 1 to the mutex, and return 0 if the call is successful.

⑦InitializeCriticalSection function: Initialize the critical section object.

⑧EnterCriticalSection function: Wait for the ownership of the specified critical section object.

⑨LeaceCriticalSettion function: Release the specified critical section object pointer.

Source code appendix:




#include "windows.h"
#include <conio.h>
#include <stdlib.h>
#include <fstream.h>
#include <io.h>
#include <string.h>
#include <stdio.h>
#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 readcount=0; // 读者数目
int writecount=0; // 写者数目
CRITICAL_SECTION RP_Write; //临界区
CRITICAL_SECTION cs_Write;
CRITICAL_SECTION cs_Read;
struct ThreadInfo
{
int serial; // 线程序号
char entity; //线程类别(判断读者线程还是写者线程)
double delay;
double persist;
};
///
// 读者优先--读者线程
//p:读者线程信息
void RP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex;
h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount");
DWORD wait_for_mutex; //等待互斥变量所有权
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
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 sents the reading require.\n",m_serial);
// 等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目增加
readcount++;
if(readcount==1)
{
 //第一个读者,等待资源
 EnterCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
//等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目减少
readcount--;
if(readcount==0)
{
 //如果所有读者读完,唤醒写者
 LeaveCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
}



///
// 读者优先--写者线程
//p:写者线程信息
void RP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
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 sents the writing require.\n",m_serial);
// 等待资源
EnterCriticalSection(&RP_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finished Writing to the file.\n",m_serial);
//释放资源
LeaveCriticalSection(&RP_Write);
}



///
// 读者优先处理函数
//file:文件名
void ReaderPriority(char* file)
{
DWORD n_thread=0; //线程数目
DWORD thread_ID; //线程 ID
DWORD wait_for_all; //等待所有线程结束
//互斥对象
HANDLE h_Mutex;
h_Mutex=CreateMutex(NULL,FALSE,"mutex_for_readcount");
//线程对象的数组
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0; // 初始化 readcount
InitializeCriticalSection(&RP_Write); //初始化临界区
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Reader Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 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].entity==READER || thread_info[i].entity=='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 reader and writer have finished operating.\n");
}
///
// 写者优先--读者线程
//p:读者线程信息
void WP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex1;
h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex1");
HANDLE h_Mutex2;
h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex2");
DWORD wait_for_mutex1; //等待互斥变量所有权
DWORD wait_for_mutex2;
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
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 sents the reading require.\n",m_serial);
wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);
//进入读者临界区
 EnterCriticalSection(&cs_Read);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
//修改读者数目
readcount++;
if(readcount==1)
{
 //如果是第一个读者,等待写者写完
 EnterCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号 mutex2
// 让其他读者进入临界区
LeaveCriticalSection(&cs_Write);
ReleaseMutex(h_Mutex1);
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
readcount--;
if(readcount==0)
{
 // 最后一个读者,唤醒写者
 LeaveCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号
}
///
// 写者优先--写者线程
//p:写者线程信息
void WP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
DWORD wait_for_mutex3;
//互斥对象
HANDLE h_Mutex3;
h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex3");
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
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 sents the writing require.\n",m_serial);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount++; //修改读者数目
if(writecount==1)
{
 //第一个写者,等待读者读完
 EnterCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
//进入写者临界区
EnterCriticalSection(&cs_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finishing Writing to the file.\n",m_serial);
//离开临界区
LeaveCriticalSection(&cs_Write);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount--; //修改读者数目
if(writecount==0)
{
 //写者写完,读者可以读
 LeaveCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
}
///
// 写者优先处理函数
//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];
readcount=0; // 初始化 readcount
writecount=0; // 初始化 writecount
InitializeCriticalSection(&cs_Write); //初始化临界区
InitializeCriticalSection(&cs_Read);
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Writer Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 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].entity==READER || thread_info[i].entity=='R')
 {
 //创建读者线程
 
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&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 reader and writer have finished operating.\n");
}
///
//主函数
int main(int argc,char* argv[])
{
char ch;
while (true)
{
 //打印提示信息
 printf("************************************************\n");
 printf(" 1:Reader Priority\n");
 printf(" 2:Writer Priority\n");
 printf(" 3:Exit Priority\n");
 printf("************************************************\n");
 printf("Enter your choice(1,2 or 3): ");
 //如果输入信息不正确,继续输入
 do{
 ch=(char)_getch( );
 }while(ch != '1' &&ch != '2' && ch != '3');
 system("cls");
 //选择 3,返回
 if(ch=='3')
 return 0;
 //选择 1,读者优先
 else if(ch=='1')
 ReaderPriority("thread.dat");
 //选择 2,写者优先
 else if(ch=='2')
 WriterPriority("thread.dat");
 //结束
 printf("\nPress Any Key To Continue: ");
 _getch( );
 system("cls");
}
return 0;
}

Guess you like

Origin blog.csdn.net/cangzhexingxing/article/details/124856879