预备知识:
函数名: vfprintf
功 能: 格式化的数据输出到指定的数据流中
用 法: int vfprintf(FILE *stream, char *format, va_list param);
函数说明:vfprintf()会根据参数format字符串来转换并格式化数据,然后将结果输出到参数stream指定的文件中,直到出现字符串结束(‘\0’)为止。关于参数format字符串的格式请参考printf()。
返回值:成功则返回实际输出的字符数,失败则返回-1,错误原因存于errno中。
---------------------------------------------------------------------------------------
下面内容来自:http://blog.csdn.net/morewindows/article/details/7596034
读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。
上面是读者写者问题示意图,类似于生产者消费者问题的分析过程,首先来找找哪些是属于“等待”情况。
第一.写者要等到没有读者时才能去写文件。
第二.所有读者要等待写者完成写文件后才能去读文件。
找完“等待”情况后,再看看有没有要互斥访问的资源。由于只有一个写者而读者们是可以共享的读文件,所以按题目要求并没有需要互斥访问的资源。类似于上一篇中美观的彩色输出,我们对生产者输出代码进行了颜色设置(在控制台输出颜色设置参见《VC 控制台颜色设置》)。因此在这里要加个互斥访问,不然很有可能在写者线程将控制台颜色设置还原之前,读者线程就已经有输出了。所以要对输出语句作个互斥访问处理,修改后的读者及写者的输出函数如下所示:
//读者线程输出函数 void ReaderPrintf(char *pszFormat, ...) { va_list pArgList; va_start(pArgList, pszFormat); EnterCriticalSection(&g_cs); vfprintf(stdout, pszFormat, pArgList); LeaveCriticalSection(&g_cs); va_end(pArgList); } //写者线程输出函数 void WriterPrintf(char *pszStr) { EnterCriticalSection(&g_cs); SetConsoleColor(FOREGROUND_GREEN); printf(" %s\n", pszStr); SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); LeaveCriticalSection(&g_cs); }
解决了互斥输出问题,接下来再考虑如何实现同步问题。可以设置一个变量来记录正在读文件的读者个数,第一个开始读文件的读者要负责将关闭允许写者进入的标志,最后一个结束读文件的读者要负责打开允许写者进入的标志。这样第一种“等待”情况就解决了。第二种“等待”情况是有写者进入时所以读者不能进入,使用一个事件就可以完成这个任务了——所有读者都要等待这个事件而写者负责触发事件和设置事件为未触发。
#include <stdio.h> #include <process.h> #include <Windows.h> BOOL SetConsoleColor(WORD wAttributes) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if(hConsole == INVALID_HANDLE_VALUE) return FALSE; return SetConsoleTextAttribute(hConsole, wAttributes); } const int READER_NUM = 5; //读者个数 //关键段和事件 CRITICAL_SECTION g_cs, g_cs_writer_count; HANDLE g_hEventWriter, g_hEventNoReader; int g_nReaderCount; //读者线程输出函数 void ReaderPrintf(char *pszFormat, ...) { va_list pArgList; va_start(pArgList, pszFormat); EnterCriticalSection(&g_cs); //为了颜色显示的统一 读者和写者不可同时输出信息 vfprintf(stdout, pszFormat, pArgList); LeaveCriticalSection(&g_cs); va_end(pArgList); } //读者线程函数 unsigned int __stdcall ReaderThreadFun(PVOID pM) { ReaderPrintf(" 编号为%d的读者进入等待中...\n", GetCurrentThreadId()); //等待写者完成 WaitForSingleObject(g_hEventWriter, INFINITE); //读者个数增加 EnterCriticalSection(&g_cs_writer_count); g_nReaderCount++; if(g_nReaderCount == 1) ResetEvent(g_hEventNoReader); LeaveCriticalSection(&g_cs_writer_count); //读取文件 ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId()); Sleep(rand() % 100); //结束阅读,读者个数减少,空位增加 ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId()); //读者个数减少 EnterCriticalSection(&g_cs_writer_count); g_nReaderCount--; if(g_nReaderCount == 0) SetEvent(g_hEventNoReader); LeaveCriticalSection(&g_cs_writer_count); return 0; } //写者线程输出函数 void WriterPrintf(char *pszStr) { EnterCriticalSection(&g_cs); SetConsoleColor(FOREGROUND_GREEN); printf(" %s\n", pszStr); SetConsoleColor(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); LeaveCriticalSection(&g_cs); } //写者线程函数 unsigned int __stdcall WriterThreadFun(PVOID pM) { WriterPrintf("写者线程进入等待中..."); //等待读文件的读者为0 WaitForSingleObject(g_hEventNoReader, INFINITE); //标记写者正在写文件 ResetEvent(g_hEventWriter); //写文件 WriterPrintf(" 写者开始写文件..."); Sleep(rand()%100); WriterPrintf(" 写者结束写文件"); //标记写文件结束 SetEvent(g_hEventWriter); return 0; } int main() { //初始化事件和关键段 InitializeCriticalSection(&g_cs); InitializeCriticalSection(&g_cs_writer_count); //手动置位,初始已触发 g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL); g_hEventNoReader = CreateEvent(NULL, FALSE, TRUE, NULL); g_nReaderCount = 0; int i; HANDLE hThread[READER_NUM + 1]; //先启动两个读者线程 for(i = 1; i <= 2; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun , NULL, 0, NULL); //启动写者线程 hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL); Sleep(50); //最后启动其它读者结程 for ( ; i <= READER_NUM; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE); for(i = 0; i < READER_NUM + 1; i++) CloseHandle(hThread[i]); //销毁事件和信号量 CloseHandle(g_hEventWriter); CloseHandle(g_hEventNoReader); DeleteCriticalSection(&g_cs); DeleteCriticalSection(&g_cs_writer_count); return 0; }
根据结果可以看出当有读者在读文件时,写者线程会进入等待状态中。当写者线程在写文件时,读者线程也会排队等待,说明读者和写者已经完成了同步。
有用评论,代码有bug