分享
 
 
 

线程同步的一些常见模式(1)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

本篇文章说明了一种多线程编程中常见的模式,该模式主要描述如下:

1.有一所幼儿园,有若干个老师和很多的孩子,有一个迷宫给孩子们玩

2.老师可以布置迷宫。

3.当某个老师在布置迷宫的时候,为了安全,孩子们不可以在迷宫里

4.不能让一个以上的老师同时布置迷宫,免得把迷宫弄乱

5.在没有老师布置迷宫的时候,孩子们可以自由进出迷宫,在里面玩

6.当某个老师想进入迷宫的时候,他必须挂一块牌子,表示老师要清理迷宫,不让孩子们再进来(但是已经在迷宫里的孩子可以继续玩),当迷宫里已经没有孩子后,老师就可以整理迷宫了,整理完后,老师就可以把牌子摘掉

或者可以这样说:

1.有一个共享的资源

2.一个或者多个线程写该资源(最常见的是一个写线程,如果想改为多个线程写很简单,大多数时候只是加一个关键代码段)

3.在读线程和写线程之间保持互斥

4.写线程之间要保证互斥

5.因为效率的原因,读线程之间不需要保持互斥.

6.因为常常有很多的读线程,写线程必须采取一定的措施,防止自己得不到机会调度

-----------------------------------------------------------------

MSDN上有一个例子,,在这个程序里,是一个写入线程,多个读线程,每个线程都有一个event对象,使用了WaitForMultipleObjects保持互斥

1.这是创建线程的代码段:

#define NUMTHREADS 4

HANDLE hGlobalWriteEvent;

void CreateEventsAndThreads(void)

{

HANDLE hReadEvents[NUMTHREADS], hThread;

DWORD 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\n", 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\n", 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!\n");

::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!\n");

::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 <windows.h>

#include <stdio.h>

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!\n");

if( m_wlock[0] == NULL )perror("m_wlock init Error!\n");

if( m_wlock[1] == NULL )perror("m_wlock init Error!\n");

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!\n");

::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!\n");

::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_)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有