執行緒同步的一些常見模式(1) (轉)
本篇文章說明了一種多執行緒中常見的,該模式主要描述如下:
1.有一所幼兒園,有若干個老師和很多的孩子,有一個迷宮給孩子們玩
2.老師可以佈置迷宮。
3.當某個老師在佈置迷宮的時候,為了,孩子們不可以在迷宮裡
4.不能讓一個以上的老師同時佈置迷宮,免得把迷宮弄亂
5.在沒有老師佈置迷宮的時候,孩子們可以自由進出迷宮,在裡面玩
6.當某個老師想進入迷宮的時候,他必須掛一塊牌子,表示老師要清理迷宮,不讓孩子們再進來(但是已經在迷宮裡的孩子可以繼續玩),當迷宮裡已經沒有孩子後,老師就可以整理迷宮了,整理完後,老師就可以把牌子摘掉
或者可以這樣說:
1.有一個共享的資源
2.一個或者多個執行緒寫該資源(最常見的是一個寫執行緒,如果想改為多個執行緒寫很簡單,大多數時候只是加一個關鍵程式碼段)
3.在讀執行緒和寫執行緒之間保持互斥
4.寫執行緒之間要保證互斥
5.因為的原因,讀執行緒之間不需要保持互斥.
6.因為常常有很多的讀執行緒,寫執行緒必須採取一定的措施,防止自己得不到機會排程
-----------------------------------------------------------------
MSDN上有一個例子,,在這個裡,是一個寫入執行緒,多個讀執行緒,每個執行緒都有一個event,使用了WaitForMultiples保持互斥
1.這是建立執行緒的程式碼段:
#define NUMTHREADS 4
HANDLE hGlobalWriteEvent;
void CreateEventsAndThreads(void)
{
HANDLE hReadEvents[NUMTHREADS], hThread;
D i, IDThread;
// Create a manual-reset event object. The master thread sets
// this to nonsignaled when it writes to the shared buffer.
//寫入資源時防止其他執行緒讀取的event
hGlobalWriteEvent = CreateEvent(
NULL, // no security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
"WriteEvent" // object name
);
if (hGlobalWriteEvent == NULL) {
// error exit
}
// Create multiple threads and an auto-reset event object
// for each thread. Each thread sets its event object to
// signaled when it is not reading from the shared buffer.
for(i = 1; i <= NUMTHREADS; i++)
{
// Create the auto-reset event.
hReadEvents[i] = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
TRUE, // initial state is signaled
NULL); // object not named
if (hReadEvents[i] == NULL)
{
// Error exit.
}
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) ThreadFunction,
&hReadEvents[i], // pass event handle
0, &IDThread);
if (hThread == NULL)
{
// Error exit.
}
}
}
2.這是寫入資源的程式碼段,從上下文來看應該是工作在主執行緒:
VOID WriteToBuffer(VOID)
{
DWORD dwWaitResult, i;
// Reset hGlobalWriteEvent to nonsignaled, to block readers.
//阻塞讀取執行緒
if (! ResetEvent(hGlobalWriteEvent) )
{
// Error exit.
}
// Wait for all reading threads to finish reading.
dwWaitResult = WaitForMultipleObjects( //等待所有讀執行緒完成
NUMTHREADS, // number of handles in array
hReadEvents, // array of read-event handles
TRUE, // wait until all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// All read-event objects were signaled.
case WAIT_OBJECT_0:
// Write to the shared buffer.
break;
// An error occurred.
default:
printf("Wait error: %d
", GetLastError());
ExitProcess(0);
}
// Set hGlobalWriteEvent to signaled.
if (! SetEvent(hGlobalWriteEvent) )
{
// Error exit.
}
// Set all read events to signaled.
for(i = 1; i <= NUMTHREADS; i++)
if (! SetEvent(hReadEvents[i]) ) {
// Error exit.
}
}
3.這是讀取執行緒的程式碼:
VOID ThreadFunction(LPVOID lpParam)
{
DWORD dwWaitResult;
HANDLE hEvents[2];
hEvents[0] = *(HANDLE*)lpParam; // thread's read event
hEvents[1] = hGlobalWriteEvent;
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hEvents, // array of event handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// Both event objects were signaled.
case WAIT_OBJECT_0:
// Read from the shared buffer.
break;
// An error occurred.
default:
printf("Wait error: %d
", GetLastError());
ExitThread(0);
}
// Set the read event to signaled.
if (! SetEvent(hEvents[0]) )
{
// Error exit.
}
}
--------------------------------------------------------------------------------
這個例子良好的實現了要求的功能,也並不麻煩,但我們想使用更加簡潔的方法,實現一個類,可以類似以下的方式使用
g_swmrg.WaitToRead();
讀取...
g_swmrg.DoneRead();
g_swmrg.WaitToWrite();
寫入...
g_swmrg.DoneRead();
使用關鍵程式碼段無疑是互斥最簡單的方法,但是卻不適用於本文,因為這樣的話在讀執行緒之間也有了互斥關係,造成了效率的下降。
看來必須使用兩個以上的互斥物件,當程式碼試圖寫資源時,他要保證沒有其他執行緒讀取和寫入
當程式碼試圖讀取時,他要保證沒有資源正在寫入:
我們使用兩個Event物件:m_wlock(寫入鎖),m_rlock(讀取鎖),這兩個物件初始化值是signaled state,不使用自動方式
m_rlock=::CreateEvent(NULL,
TRUE,
TRUE,
NULL);
//----------------------------------------------------
1.等待寫
void WaitToWrite()
{
::EnterCriticalSection(&m_cs);
DWORD dwWaitResult = ::WaitForSingleObject( m_rlock ,INFINITE );
if( dwWaitResult == WAIT_OBJECT_0 )
::ResetEvent( m_wlock);//不可以讀
else
printf( "WaitWrite Error!
");
::LeaveCriticalSection(&m_cs);
}
2.寫完成
void EndWrite()
{
::SetEvent( m_wlock ); //喚醒等待的讀取執行緒
}
3.等待讀
void WaitToRead()
{
::EnterCriticalSection(&m_cs);
DWORD dwWaitResult = ::WaitForSingleObject( m_wlock ,INFINITE );
if( dwWaitResult == WAIT_OBJECT_0 )
{
::ResetEvent( m_rlock );
InterlockedIncrement((long*)&m_nReadNum);//m_nReadNum++這個表示有多少個執行緒正在讀
}
else
printf( "WaitWrite Error!
");
::LeaveCriticalSection(&m_cs);
}
4.讀取完成
void EndRead()
{
::EnterCriticalSection(&m_cs);
if( 0 >= InterlockedDecrement( (long*)&m_nReadNum )) //m_nReadNum--;
::SetEvent( m_rlock );//當沒有執行緒讀時,//喚醒等待的寫執行緒
}
這個例子已經可以正常工作了,但是我們發現了一個問題,當多個執行緒讀取時,因為寫入執行緒要到活動的讀取執行緒數目為0時才可以寫入,將會很難得到機會,甚至會餓死,為解決這個問題,我們可以在寫入執行緒等待前設定一個事件(老師要掛一塊牌子),當讀取執行緒發現這個事件時就等待,修改的程式碼如下:
--------------------------------------------------------------------------------
// SRWM1.h: interface for the CSRWM1 class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_SRWM1_H__62A82971_1C04_4EC9_8A52_C0392E333575__INCLUDED_)
#define AFX_SRWM1_H__62A82971_1C04_4EC9_8A52_C0392E333575__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <.h>
#include
class CSRWM1
{
private:
HANDLE m_rlock;//讀鎖
HANDLE m_wlock[2];//寫鎖
CRITICAL_SECTION m_cs;//關鍵段
volatile long m_nReadNum;
public:
//-----------------------------------
CSRWM1()
{
m_rlock=::CreateEvent(NULL,
TRUE,
TRUE,
NULL);
m_wlock[0]=::CreateEvent(NULL,
TRUE,
TRUE,
NULL);
m_wlock[1]=::CreateEvent(NULL,
TRUE,
TRUE,
NULL);
::InitializeCriticalSection( &m_cs );
if( m_rlock == NULL )perror("m_rlock init Error!
");
if( m_wlock[0] == NULL )perror("m_wlock init Error!
");
if( m_wlock[1] == NULL )perror("m_wlock init Error!
");
m_nReadNum=0;
}
virtual ~CSRWM1()
{
::CloseHandle( m_rlock );
::CloseHandle( m_wlock[0] );
::CloseHandle( m_wlock1] );
::DeleteCriticalSection( &m_cs );
}
//--------------------------------------
void WaitToWrite()
{
::EnterCriticalSection(&m_cs);
::ResetEvent(m_wlock[1]);
DWORD dwWaitResult = ::WaitForSingleObject( m_rlock ,INFINITE );
if( dwWaitResult == WAIT_OBJECT_0 )
::ResetEvent( m_wlock[0] );
else
printf( "WaitWrite Error!
");
::LeaveCriticalSection(&m_cs);
}
void EndWrite()
{
::SetEvent( m_wlock[1] );
::SetEvent( m_wlock[0] );
}
//----------------------------------------
void WaitToRead()
{
::EnterCriticalSection(&m_cs);
DWORD dwWaitResult = ::WaitForMultipleObjects( 2, m_wlock ,TRUE ,INFINITE );
if( dwWaitResult == WAIT_OBJECT_0 )
{
::ResetEvent( m_rlock );
InterlockedIncrement((long*)&m_nReadNum);
}
else
printf( "WaitWrite Error!
");
::LeaveCriticalSection(&m_cs);
}
void EndRead()
{
if( 0 >= InterlockedDecrement( (long*)&m_nReadNum ))
::SetEvent( m_rlock );
}
};
#endif // !defined(AFX_SRWM1_H__62A82971_1C04_4EC9_8A52_C0392E333575__INCLUDED_)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-956319/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 理解常見的執行緒同步設施執行緒
- 多執行緒-多執行緒常見的面試題執行緒面試題
- 多執行緒-執行緒的狀態轉換圖及常見執行情況執行緒
- linux執行緒同步方式是什麼?常見的有哪些?Linux執行緒
- 擴充套件Delphi的執行緒同步物件(1) (轉)套件執行緒物件
- 執行緒池以及四種常見執行緒池執行緒
- 常見的四種執行緒池執行緒
- VC++ 執行緒同步(轉)C++執行緒
- Java面試中,一些常見的有關多執行緒問題!Java面試執行緒
- 執行緒8--GCD常見用法執行緒GC
- 執行緒的同步執行緒
- .NET多執行緒程式設計(3):執行緒同步 (轉)執行緒程式設計
- POSIX執行緒程式設計起步(2)-執行緒同步 (轉)執行緒程式設計
- Posix執行緒程式設計指南(3)-執行緒同步 (轉)執行緒程式設計
- 1、多執行緒同步——CPU、core核、執行緒、記憶體執行緒記憶體
- Java執行緒:執行緒的同步與鎖Java執行緒
- c#執行緒-執行緒同步C#執行緒
- 執行緒同步及執行緒鎖執行緒
- 執行緒同步執行緒
- 【面經】多執行緒常見面試題執行緒面試題
- 多執行緒併發常見問題執行緒
- COM元件中的執行緒模式 (轉)元件執行緒模式
- 多執行緒和多執行緒同步執行緒
- MySQL主執行緒、從I/O執行緒和從SQL執行緒的State列常見狀態介紹MySql執行緒
- java執行緒學習5——執行緒同步之同步方法Java執行緒
- Java常見知識點彙總(⑬)——執行緒Java執行緒
- 執行緒與同步非同步執行緒非同步
- 理解執行緒同步執行緒
- 深入執行緒同步執行緒
- Java—執行緒同步Java執行緒
- 多執行緒同步執行緒
- .net執行緒同步執行緒
- 執行緒同步方法執行緒
- 多執行緒下的程式同步(執行緒同步問題總結篇)執行緒
- Java多執行緒設計模式(1)Java執行緒設計模式
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- Java中的執行緒同步Java執行緒
- 多執行緒同步的原理執行緒