zoukankan      html  css  js  c++  java
  • 操作系统 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 }
  • 相关阅读:
    关于在前台.aspx页面中应用变量的方法
    web.config中配置数据库连接字符串
    PHP性能优化:APC可选PHP缓存
    Laruence谈:深入理解Javascript之this关键字
    用 yum 安装 Apache、Mysql、PHP
    Linux下which、whereis、locate、find 命令的区别
    时间复杂度、空间复杂度
    yum 和 aptget 用法及区别
    How browsers work
    Linux File Permission
  • 原文地址:https://www.cnblogs.com/HenryThinker/p/2818858.html
Copyright © 2011-2022 走看看