操作系统 lab3 笔记
操作系统 Lab3
昨天和前天把MoreWindows的秒杀线程系列看了个大部分,今天去图书馆自己动手写了下读者写着问题,几乎写了一整天,太挫了。当然收获也很多。
读者写者问题是很经典的进程/线程同步问题,在MoreWindows那里介绍的是一写者与多读者的情况,读者与写者的优先级一样。多个读者可以同时读取,读的时候写者不能写,写的时候读者不能读。Lab3这的要求多一些,情况复杂一些。是多读者多写者的情况,并且规定写者有较高优先级。因为太挫了,所以暂时只整了个同等优先级的情况。先记录着。我是用critical section来实现互斥,用event来实现同步。(有空再用mutex和semaphore实现一下)
我从原先看的一写者的情况开始构思,想着能不能直接把写者数量加上去就可以跑了。发现不行,因为
(1)等待的写者要等正在写的写者写完
(2)等待的写者要等所有正在读的读者读完
这两种同步逻辑上就是不一样的,用一个event容易出问题。
关于互斥没多想,只想着reader数目增加和自定义输出的时候要开个关键域包着,没想到漏了一个(后面说)。
本来打算把自定义输出的关键域放在封装好的输出函数里的,后来发现不行,因为输出不但要设置颜色,输出完还要变回来,所以要用关键域把整个输出的语句块给包住,也就是:
1 EnterCriticalSection( &csConsoleColor ); 2 SetConsoleColor( FOREGROUND_GREEN | FOREGROUND_RED ); 3 int countOfOutput = vfprintf( stdout, format, pArgList); 4 SetConsoleColor( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 5 LeaveCriticalSection( &csConsoleColor );
这样就可以保证输出完在变颜色了(我读者是白色,写者是黄色)
貌似还出现过一个问题,就是:
for ( ; writec<WRITER_NUMBER; writec++ ) { writer[writec] = ( HANDLE )_beginthreadex( NULL, 0, WriterThread, NULL, 0, NULL ); Sleep( rand()%100 ); }
这的WRITER_NUMBER当时从上面copy过来的时候忘了改了(原来是2)。巨坑爹因为这个小错,程序跑出了很奇葩的结果,写者正在写者,程序就结束了。应该是写者被重新初始化的原因。这里提醒自己:一旦碰到无法解释的奇葩错误,先检查下自己的代码里有没有变量名函数名写错、copy完忘了改特定部分的错误。当时一度以为WaitForMultipleObjects这个函数的使用出了问题。后来验证过,没有。这个函数就是等待指定handle上的所有内核变量变成触发态,就是signaled.
另一个改动是h_e_reader = CreateEvent( NULL, TRUE, TRUE, NULL ); 把第二个参数改成TRUE了,意思是手动设置。就是在WaitForSingleObject( h_e_reader, INFINITE ); 之后不会自动调用ResetEvent( h_e_reader )。逻辑上我认为也不需要,写者开始写的时候,读者依然是没有在读的,所以自然不应该自动重置。
改到现在,发现在运行几个读写者之后,会出现:
1023号读者正在读取
2420号写者正在写入
2223号读者正在读取
3215号读者正在读取
这种问题,是逻辑出了问题,仔细检查后,发现了问题。
问题在于写者的WaitForSingleObject( h_e_reader, INFINITE );
和读者的WaitForSingleObject( h_e_writer, INFINITE );
假设读者线程在跑完WaitForSingleObject( h_e_writer, INFINITE ); 后,操作系统立即切换到写者线程,然后写者跑WaitForSingleObject( h_e_reader, INFINITE ); 这样,就会出现同时读写的问题。解决方法很简单,就是把读写者的WaitForSingleObject一直到下面ResetEvent和输出一起用一个关键域包住。这样就不会出现同时读写的情况。至此问题得到解决。
还是自己打代码来的印象深刻,和copy完再改是完全不一样的。
代码:
1 #include <stdio.h> 2 #include <stdarg.h> 3 #include <Windows.h> 4 #include <process.h> 5 6 7 int ReaderPrintf( char* format, ... ); 8 int WriterPrintf( char* format, ... ); 9 bool SetConsoleColor( WORD outAttributes ); 10 unsigned int __stdcall ReaderThread( PVOID p ); 11 unsigned int __stdcall WriterThread( PVOID p ); 12 13 14 //关键段和event的声明 15 CRITICAL_SECTION csConsoleColor, csReaderCount, csReadWrite; 16 HANDLE h_e_reader, h_e_writer, h_e_writerwriter; 17 18 //有多少个正着读的读者 19 int readerCount; 20 21 //读写者数目 22 const int READER_NUMBER = 20; 23 const int WRITER_NUMBER = 12; 24 25 void main() 26 { 27 //for循环里的递增量 28 int readc, writec; 29 30 readerCount = 0; 31 32 //关键段和event的初始化 33 InitializeCriticalSection( &csConsoleColor ); 34 InitializeCriticalSection( &csReaderCount ); 35 InitializeCriticalSection( &csReadWrite ); 36 h_e_reader = CreateEvent( NULL, TRUE, TRUE, NULL ); 37 h_e_writer = CreateEvent( NULL, TRUE, TRUE, NULL ); 38 h_e_writerwriter = CreateEvent( NULL, FALSE, TRUE, NULL ); 39 40 //读写者的handle 41 HANDLE reader[READER_NUMBER]; 42 HANDLE writer[WRITER_NUMBER]; 43 44 //先初始化10个读者 45 for( readc = 0; readc<10; readc++ ) 46 { 47 reader[readc] = ( HANDLE )_beginthreadex( NULL, 0, ReaderThread, NULL, 0, NULL ); 48 Sleep( rand()%100 ); 49 } 50 51 //初始化6个写者 52 for ( writec = 0; writec<6; writec++ ) 53 { 54 writer[writec] = ( HANDLE )_beginthreadex( NULL, 0, WriterThread, NULL, 0, NULL ); 55 //Sleep( rand()%100 ); 56 } 57 58 //Sleep( 50 ); 59 60 //初始化剩下的读者 61 for( ; readc<READER_NUMBER; readc++ ) 62 { 63 reader[readc] = ( HANDLE )_beginthreadex( NULL, 0, ReaderThread, NULL, 0, NULL ); 64 Sleep( rand()%50 ); 65 } 66 67 //初始化剩下的写者 68 for ( ; writec<WRITER_NUMBER; writec++ ) 69 { 70 writer[writec] = ( HANDLE )_beginthreadex( NULL, 0, WriterThread, NULL, 0, NULL ); 71 Sleep( rand()%100 ); 72 } 73 74 //等待所有内核变量的signal 75 WaitForMultipleObjects( READER_NUMBER, reader, TRUE, INFINITE ); 76 WaitForMultipleObjects( WRITER_NUMBER, writer, TRUE, INFINITE ); 77 78 79 //printf("Already here! \n"); 80 81 //回收 82 DeleteCriticalSection( &csConsoleColor ); 83 DeleteCriticalSection( &csReaderCount ); 84 DeleteCriticalSection( &csReadWrite ); 85 for( int i = 0; i<READER_NUMBER; i++ ) 86 CloseHandle( reader[i] ); 87 for( int i = 0; i<WRITER_NUMBER; i++) 88 CloseHandle( writer[i] ); 89 90 } 91 92 //实现读者输出,字体颜色:白 93 int ReaderPrintf( char* format, ... ) 94 { 95 va_list pArgList; 96 97 va_start( pArgList, format ); 98 EnterCriticalSection( &csConsoleColor ); 99 int countOfOutput = vfprintf( stdout, format, pArgList ); 100 LeaveCriticalSection( &csConsoleColor ); 101 va_end( pArgList ); 102 103 return countOfOutput; 104 } 105 106 //实现写者输出,字体颜色:黄 107 int WriterPrintf( char* format, ... ) 108 { 109 va_list pArgList; 110 111 va_start( pArgList, format ); 112 EnterCriticalSection( &csConsoleColor ); 113 SetConsoleColor( FOREGROUND_GREEN | FOREGROUND_RED ); 114 int countOfOutput = vfprintf( stdout, format, pArgList); 115 SetConsoleColor( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 116 LeaveCriticalSection( &csConsoleColor ); 117 va_end( pArgList ); 118 119 return countOfOutput; 120 } 121 122 123 //封装控制台字体颜色控制的函数 124 bool SetConsoleColor( WORD outAttributes ) 125 { 126 HANDLE console = GetStdHandle( STD_OUTPUT_HANDLE ); 127 if( console == INVALID_HANDLE_VALUE ) 128 return false; 129 130 SetConsoleTextAttribute( console, outAttributes ); 131 132 return true; 133 } 134 135 //读者线程 136 unsigned int __stdcall ReaderThread( PVOID p ) 137 { 138 ReaderPrintf( "%d号读者正在等待读取\n", GetCurrentThreadId() ); 139 140 EnterCriticalSection( &csReadWrite ); 141 WaitForSingleObject( h_e_writer, INFINITE ); 142 143 EnterCriticalSection( &csReaderCount ); 144 readerCount++; 145 if( readerCount > 0 ) 146 ResetEvent( h_e_reader ); 147 LeaveCriticalSection( &csReaderCount ); 148 149 ReaderPrintf( "%d号读者正在进行读取\n", GetCurrentThreadId() ); 150 LeaveCriticalSection( &csReadWrite ); 151 152 153 Sleep( rand()%100 ); 154 155 ReaderPrintf( "%d号读者读取完毕\n", GetCurrentThreadId() ); 156 157 EnterCriticalSection( &csReaderCount ); 158 readerCount--; 159 if( readerCount == 0 ) 160 SetEvent( h_e_reader ); 161 LeaveCriticalSection( &csReaderCount ); 162 163 return 0; 164 } 165 166 //写者线程 167 unsigned int __stdcall WriterThread( PVOID p ) 168 { 169 WriterPrintf( "%d号写者正在等待写入\n", GetCurrentThreadId() ); 170 171 EnterCriticalSection( &csReadWrite ); 172 WaitForSingleObject( h_e_reader, INFINITE ); 173 WaitForSingleObject( h_e_writerwriter, INFINITE ); 174 175 ResetEvent( h_e_writer ); 176 177 WriterPrintf( "%d号写者正在进行写入\n", GetCurrentThreadId() ); 178 LeaveCriticalSection( &csReadWrite ); 179 180 181 Sleep( rand()%100 ); 182 183 184 WriterPrintf( "%d号写者写入完毕\n", GetCurrentThreadId() ); 185 SetEvent( h_e_writer ); 186 SetEvent( h_e_writerwriter ); 187 188 return 0; 189 }