操作系统 lab3 笔记

操作系统 Lab3

 

昨天和前天把MoreWindows的秒杀线程系列看了个大部分,今天去图书馆自己动手写了下读者写着问题,几乎写了一整天,太挫了。当然收获也很多。

 

读者写者问题是很经典的进程/线程同步问题,在MoreWindows那里介绍的是一写者与多读者的情况,读者与写者的优先级一样。多个读者可以同时读取,读的时候写者不能写,写的时候读者不能读。Lab3这的要求多一些,情况复杂一些。是多读者多写者的情况,并且规定写者有较高优先级。因为太挫了,所以暂时只整了个同等优先级的情况。先记录着。我是用critical section来实现互斥,用event来实现同步。(有空再用mutexsemaphore实现一下)

 

我从原先看的一写者的情况开始构思,想着能不能直接把写者数量加上去就可以跑了。发现不行,因为

(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 }

 

 

posted @ 2012-12-15 00:41  CheckMate  阅读(369)  评论(0编辑  收藏  举报