天天看點

win32線程建立、線程同步以及讀者寫者問題

include "stdafx.h" 
#include <stdio.h>  
#include <windows.h>  
#include <process.h>

//設定控制台輸出顔色  
BOOL SetConsoleColor(WORD wAttributes)  
{  
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);  
	if (hConsole == INVALID_HANDLE_VALUE)  
		return FALSE;  

	return SetConsoleTextAttribute(hConsole, wAttributes);  
}

//設定控制台輸出藍色字型
void ConsolePrintBlue(char *pszFormat, ...)  
{  
	va_list   pArgList;  
	va_start(pArgList, pszFormat);  
	SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN);
	vfprintf(stdout, pszFormat, pArgList); 
	SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); 
	va_end(pArgList);  
}

//線程函數
unsigned int __stdcall Threadfun(LPVOID lpParam)  
{  
	LPDWORD pData = (LPDWORD)lpParam;

	for(int i = 0;i < 10; i++)  
	{  
		Sleep(100);  //子線程挂起100毫秒
		printf("Thread_ID = %d , \tParameters = %d, \ti = %d\n", GetCurrentThreadId(), *pData, i);  
	}  
	_endthreadex(0);  
	return 0;  
}  

//如果在代碼中有使用标準C運作庫中的函數時,盡量使用_beginthreadex()來代替CreateThread()
void ThreadexTest()
{
	ConsolePrintBlue("--------- 進入線程建立、挂起、關閉測試 ---------\n");
	DWORD Para0, Para1;  // 設定線程傳遞參數
	HANDLE hThread[2];  
	Para0 = 0; Para1 = 1;   
	
	//建立線程0并挂起
	hThread[0] = (HANDLE)_beginthreadex(NULL, 0, Threadfun, &Para0, CREATE_SUSPENDED, NULL);  
	hThread[1] = (HANDLE)_beginthreadex(NULL, 0, Threadfun, &Para1, 0, NULL);  
	
	Sleep(110);  //主線程挂起110毫秒,期間線程1正好可以輸出一次
	ResumeThread(hThread[0]);    //恢複線程0
	SuspendThread(hThread[1]);   //挂起線程1
	Sleep(510); //主線程挂起510毫秒,期間線程0正好可以輸出5次

	//終止線程0的運作
	TerminateThread(hThread[0], 0);  //并不保證線程被終止,最好采用線程函數傳回方式終止
	ResumeThread(hThread[1]);        //恢複線程1
	WaitForSingleObject(hThread[1], INFINITE);   //等待線程1執行完
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);

	ConsolePrintBlue("--------- 結束線程建立、挂起、關閉測試 ---------\n\n\n");
}

CRITICAL_SECTION g_cs;
//關鍵段線程函數
unsigned int _stdcall  CriticalSection(LPVOID lpParam)
{ 
	EnterCriticalSection(&g_cs);//進入關鍵段
	printf("Thread_ID%d start\n", GetCurrentThreadId());
	ConsolePrintBlue("挂起線程100毫秒\n");
	Sleep(100);  //挂起線程100毫秒
	printf("Thread_ID%d end\n\n", GetCurrentThreadId());
	LeaveCriticalSection(&g_cs);//離開關鍵段
	_endthreadex(0);
	return 0;
}

//關鍵段測試函數
void CriticalSectionTest()
{
	ConsolePrintBlue("\n--------- 使用臨界區(CRITICAL_SECTION及其系列函數)實作線程同步 ---------\n");
	InitializeCriticalSection(&g_cs);    //初始化g_cs的成員 
	
	HANDLE hThread[10]; 
	for(int i = 0; i < 10; i++)
	{ 
		hThread[i] = (HANDLE)_beginthreadex(NULL, 0, CriticalSection, NULL, 0, NULL);  
	}

	WaitForMultipleObjects(10, hThread, TRUE, INFINITE);   
	for(int i = 0; i < 10; i++)
	{
		CloseHandle(hThread[i]);
	} 
	DeleteCriticalSection(&g_cs);//删除關鍵段

	ConsolePrintBlue("\\----- end ------\n\n\n");
}


RTL_SRWLOCK  lock;
//互斥鎖線程函數
unsigned int _stdcall  SrwLock(LPVOID lpParam)
{ 
	AcquireSRWLockExclusive(&lock);  //進入讀寫鎖
	printf("Thread_ID%d start\n", GetCurrentThreadId());
	ConsolePrintBlue("挂起線程100毫秒\n");
	Sleep(100);  //挂起線程100毫秒
	printf("Thread_ID%d end\n\n", GetCurrentThreadId());
	ReleaseSRWLockExclusive(&lock);  //結束讀寫鎖
	_endthreadex(0);
	return 0;
}

//互斥鎖測試函數
void SrwLockTest()
{
	ConsolePrintBlue("\n--------- 使用互斥鎖(RTL_SRWLOCK及其系列函數)實作線程同步 ---------\n");
	HANDLE hThread[10]; 
	InitializeSRWLock(&lock);//初始化lock的成員 
	for(int i = 0; i < 10; i++)
	{ 
		hThread[i] = (HANDLE)_beginthreadex(NULL, 0, SrwLock, NULL, 0, NULL);  
	}

	WaitForMultipleObjects(10, hThread, TRUE, INFINITE);   
	ConsolePrintBlue("\\----- end ------\n\n\n");
}


HANDLE semaphore;
//信号量線程函數
unsigned int _stdcall  Semaphore(LPVOID lpParam)
{
	WaitForSingleObject(semaphore, INFINITE);  //執行WaitForSingleObject一次,計數值就減一 
	printf("Thread_ID%d start\n", GetCurrentThreadId());
	ConsolePrintBlue("挂起線程100毫秒\n");
	Sleep(100);
	printf("Thread_ID%d end\n\n", GetCurrentThreadId());
	ReleaseSemaphore(semaphore, 1, NULL);    //計數加一
	_endthreadex(0);
	return 0;
}

//信号量測試函數
void SemaphoreTest()
{
	ConsolePrintBlue("\n--------- 使用信号量核心對象實作線程同步 ---------\n");
	HANDLE hThread[10];
	semaphore = CreateSemaphore(NULL, 1, 1, NULL);  //初始計數為1,最大計數為1
	
	for(int i = 0; i < 10; i++)
	{ 
		hThread[i] = (HANDLE)_beginthreadex(NULL, 0, Semaphore, NULL, 0, NULL);  
	}

	WaitForMultipleObjects(10, hThread, TRUE, INFINITE);   
	CloseHandle(semaphore);
	ConsolePrintBlue("\\----- end ------\n\n\n");
}

HANDLE hEvent; 
//事件線程函數
unsigned int _stdcall  Event(LPVOID lpParam)
{
	WaitForSingleObject(hEvent, INFINITE);  //自動複位,變為無信号
	printf("Thread_ID%d start\n", GetCurrentThreadId());
	ConsolePrintBlue("挂起線程100毫秒\n");
	Sleep(100);
	printf("Thread_ID%d end\n\n", GetCurrentThreadId());
	SetEvent(hEvent);    //設定為有信号
	_endthreadex(0);
	return 1;
}

//事件測試函數
void EventTest()
{
	ConsolePrintBlue("\n--------- 使用事件核心對象實作線程同步 ---------\n");
	HANDLE hThread[10];
	hEvent = CreateEvent(NULL, false, true, NULL); //初始bManualReset設定為false,bInitialState設定為有信号

	for(int i = 0; i < 10;i++)
	{
		hThread[i] =(HANDLE)_beginthreadex(NULL, 0, Event , NULL, 0, NULL);  
	}
	WaitForMultipleObjects(10, hThread, TRUE, INFINITE);   
	CloseHandle(hEvent);

	ConsolePrintBlue("\\----- end ------\n\n\n");
}


const int QUEUE_LEN = 10;  //環形隊列長度
int g_queue[QUEUE_LEN];    //環形隊列
int g_i, g_j;                
int nDataR, nDataW;       
HANDLE g_hEmpty, g_hFull;    //讀寫信号量,讀者在讀信号量不為空則可讀,寫者在寫資訊量不為滿則可寫 
HANDLE g_hEventReader, g_hEventWriter;  //讀者、寫者事件,僅能一次一個寫者進行寫操作,一個讀者進行讀操作

//讀者線程輸出函數
void ReaderPrintRed(char *pszFormat, ...)  
{  
	va_list   pArgList;  
	va_start(pArgList, pszFormat);  
	EnterCriticalSection(&g_cs);  
	SetConsoleColor(FOREGROUND_RED);
	vfprintf(stdout, pszFormat, pArgList); 
	SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); 
	LeaveCriticalSection(&g_cs);  
	va_end(pArgList);  
}  

//寫者線程輸出函數  
void WriterPrintGreen(char *pszStr, ...)  
{  
	va_list pArgList;  
	va_start(pArgList, pszStr); 
	EnterCriticalSection(&g_cs);  
	SetConsoleColor(FOREGROUND_GREEN);   
	vfprintf(stdout, pszStr, pArgList); 
	SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);  
	LeaveCriticalSection(&g_cs);  
}   

//單個讀者線程函數
unsigned int __stdcall ReaderThreadFun(PVOID pM)  
{  
	ReaderPrintRed("編号為%d的讀者開始讀取資料...\n", GetCurrentThreadId());  
	
	int nDataR = 0;  
	while (nDataR < 20)  
	{  
		WaitForSingleObject(g_hFull, INFINITE);  //等待資料隊列不為空 
		nDataR = g_queue[g_i];  
		g_i = (g_i + 1)%QUEUE_LEN;  
		ReaderPrintRed("編号為%d讀者從隊列中讀取資料%d\n", GetCurrentThreadId(), nDataR);  
		ReleaseSemaphore(g_hEmpty, 1, NULL);  
	} 

	ReaderPrintRed("編号為%d的讀者結束讀取資料//end\n", GetCurrentThreadId());  
	return 0;  
}

//單個寫者線程函數
unsigned int __stdcall WriterThreadFun(PVOID pM)  
{  
	WriterPrintGreen("編号為%d的寫者開始寫資料...\n", GetCurrentThreadId());
	
	int nDataR = 0;  
	while (nDataR < 20)  
	{  
		WaitForSingleObject(g_hEmpty, INFINITE);   //等待資料隊列有空位
		g_queue[g_j] = ++nDataR;  
		g_j = (g_j + 1)%QUEUE_LEN;  
		WriterPrintGreen("編号為%d寫者将資料%d寫入隊列\n", GetCurrentThreadId(), nDataR); 
		ReleaseSemaphore(g_hFull, 1, NULL);       //g_hFull信号量加一
	} 

	WriterPrintGreen("編号為%d的寫者結束寫資料//end\n", GetCurrentThreadId());  
	return 0;  
} 

//多個讀者線程函數
unsigned int __stdcall MultiReaderThreadFun(PVOID pM)
{
	ReaderPrintRed("編号為%d的讀者開始讀取資料...\n", GetCurrentThreadId());  
	while (nDataR < 20)  
	{  
		WaitForSingleObject(g_hEventReader, INFINITE);  //一次隻能進入一個讀者

		if (nDataR >= 20)  
		{
			SetEvent(g_hEventReader);
			ReaderPrintRed("編号為%d的讀者結束讀資料..//end\n", GetCurrentThreadId());  
			return 0;
		}
		
		WaitForSingleObject(g_hFull, INFINITE);
		nDataR = g_queue[g_i];  
		g_i = (g_i + 1)%QUEUE_LEN;  
		ReaderPrintRed("編号為%d讀者從隊列中讀取資料%d\n", GetCurrentThreadId(), nDataR);
		ReleaseSemaphore(g_hEmpty, 1, NULL);

		SetEvent(g_hEventReader);
	
	}  
	ReaderPrintRed("編号為%d的讀者結束讀取資料//end\n", GetCurrentThreadId());  
	return 0;  
}

//多個寫者線程函數
unsigned int __stdcall MultiWriterThreadFun(PVOID pM)
{   
	WriterPrintGreen("編号為%d的寫者開始寫資料...\n", GetCurrentThreadId());
	
	while (nDataW < 20)  
	{  
		WaitForSingleObject(g_hEventWriter, INFINITE);  //一次僅能進入一個讀者

		if (nDataW >= 20)
		{
			SetEvent(g_hEventWriter);
			WriterPrintGreen("編号為%d的寫者結束寫資料..//end\n", GetCurrentThreadId());  
			return 0;
		}
		
		WaitForSingleObject(g_hEmpty, INFINITE);
		g_queue[g_j] = ++nDataW;  
		g_j = (g_j + 1) % QUEUE_LEN;  
		WriterPrintGreen("編号為%d的寫者将資料%d寫入隊列\n", GetCurrentThreadId(), nDataW);  
		ReleaseSemaphore(g_hFull, 1, NULL); 	
		SetEvent(g_hEventWriter);
	}  

	WriterPrintGreen("編号為%d的寫者結束寫資料//end\n", GetCurrentThreadId());  
	return 0;  
}

//一個寫者一個讀者
void SingleReaderSingleWirterTest()
{  
	ConsolePrintBlue("\n----------- 模拟一個寫者和一個讀者之間通路環形隊列操作 ---------\n");
	
	HANDLE hThread[2]; 
	g_i = g_j = 0; 
	nDataR = nDataW = 0;

	InitializeCriticalSection(&g_cs);    //初始化g_cs的成員 
	
	g_hEmpty = CreateSemaphore(NULL, QUEUE_LEN, QUEUE_LEN, NULL);  //初始信号量為滿,最大信号量為環形隊列總個數 
	g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);   //初始信号量為空,最大信号量為環形隊列總個數  

	hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);  
	hThread[1] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);  

	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  
	
	for (int i = 0; i < 2; i++)  
	{
		CloseHandle(hThread[i]); 
	}
	CloseHandle(g_hEmpty);  
	CloseHandle(g_hFull);  
	
	ConsolePrintBlue("循環隊列中儲存的數為:\n");
	for (int i = 0; i < QUEUE_LEN; i++)
	{
		printf("%-6d", g_queue[i]);
	}
	printf("\n");

	DeleteCriticalSection(&g_cs);//删除關鍵段

	ConsolePrintBlue("\\----- end ------\n\n\n");
}

//一個寫者十個讀者
void MultiReaderSingleWirterTest()
{
	ConsolePrintBlue("\n----------- 模拟一個寫者和多個讀者之間通路緩形隊列操作 ---------\n");
	
	g_i = g_j = 0;  
	HANDLE hThreadR[10];
	HANDLE hThreadW;
	nDataR = nDataW = 0;

	InitializeCriticalSection(&g_cs);  

	g_hEmpty = CreateSemaphore(NULL, 1, QUEUE_LEN, NULL);  
	g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);  
	g_hEventReader = CreateEvent(NULL, false, true, NULL);        //設定事件,每次僅能一個讀者讀

	for (int i = 0; i < 10; i++)
	{
		hThreadR[i] = (HANDLE)_beginthreadex(NULL, 0, MultiReaderThreadFun, NULL, 0, NULL); 
	} 

	hThreadW = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);  

	WaitForSingleObject(hThreadW, INFINITE);
	WaitForMultipleObjects(10, hThreadR, TRUE, INFINITE);  

	for (int i = 0; i < 10; i++)  
	{	
		CloseHandle(hThreadR[i]); 
	}
	CloseHandle(hThreadW);
	CloseHandle(g_hEmpty);  
	CloseHandle(g_hFull);  
	
	ConsolePrintBlue("循環隊列中儲存的數為:\n");
	for (int i = 0; i < QUEUE_LEN; i++)
	{
		printf("%-6d", g_queue[i]);
	}
	printf("\n");

	DeleteCriticalSection(&g_cs);  

	ConsolePrintBlue("\\----- end ------\n\n\n");
}

//十個寫者十個讀者
void MultiReaderMultiReaderWriterTest() 
{
	ConsolePrintBlue("\n----------- 模拟多個寫者和多個讀者之間通路環形隊列操作 ---------\n");
	g_i = g_j = 0;  
	HANDLE hThreadR[10];
	HANDLE hThreadW[10];
	nDataR = nDataW = 0;

	InitializeCriticalSection(&g_cs);  
 
	g_hEventReader = CreateEvent(NULL, false, true, NULL);        //設定事件,每次僅能一個讀者讀
	g_hEventWriter = CreateEvent(NULL, false, true, NULL);        //設定事件,每次僅能一個寫者寫
	g_hEmpty = CreateSemaphore(NULL, 1, QUEUE_LEN, NULL);         //初始計數為一,最大計數為十
	g_hFull = CreateSemaphore(NULL, 0, QUEUE_LEN, NULL);          //初始計數為零,最大計數為十

	for (int i = 0; i < 10; i++)
	{
		hThreadR[i] = (HANDLE)_beginthreadex(NULL, 0, MultiReaderThreadFun, NULL, 0, NULL); 
	} 
	for(int i = 0; i < 10; i++)
	{
		hThreadW[i] = (HANDLE)_beginthreadex(NULL, 0, MultiWriterThreadFun, NULL, 0, NULL);  
	}
	
	WaitForMultipleObjects(10, hThreadR, TRUE, INFINITE);  
	WaitForMultipleObjects(10, hThreadW, TRUE, INFINITE);

	for (int i = 0; i < 10; i++)  
	{	
		CloseHandle(hThreadR[i]); 
		CloseHandle(hThreadW[i]);
	}
	CloseHandle(g_hEmpty);  
	CloseHandle(g_hFull);  
	
	ConsolePrintBlue("循環隊列中儲存的數為:\n");
	for (int i = 0; i < QUEUE_LEN; i++)
	{
		printf("%-6d", g_queue[i]);
	}
	printf("\n");

	DeleteCriticalSection(&g_cs);  

	ConsolePrintBlue("\\----- end ------\n\n\n");	
}

void main()  
{  

	ConsolePrintBlue("----------- 基于win32平台下編寫多線程操作 -----------\n\n");
	
	//建立線程,關閉線程,挂起線程
	ConsolePrintBlue("\n\n\n----------- 1:建立線程,關閉線程,挂起線程 -----------\n\n");
	ThreadexTest();

	//使用臨界資源,互斥鎖,信号量,事件
	ConsolePrintBlue("\n\n\n\n\n----------- 2:使用臨界資源,互斥鎖,信号量,事件 -----------\n\n");
	CriticalSectionTest();
 	SrwLockTest();
 	SemaphoreTest();
 	EventTest();

	//讀者寫者問題
	ConsolePrintBlue("\n\n\n\n\n----------- 3:讀者寫者問題 -----------\n\n");
 	SingleReaderSingleWirterTest();
 	MultiReaderSingleWirterTest();
	MultiReaderMultiReaderWriterTest();
} 
           

繼續閱讀