线程间的同步机制(2)

----------------------------------------------------------------------------------------
//示例代码:
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
	int startIdx = (int)lpParameter;
	for (int idx = startIdx; idx < startIdx+100; ++idx) {
		CString str;
		str.Format(_T("%d"), idx);
		g_ArrString.Add(str);
	}
	return 0;
}


void CThreadTestDlg::OnBnClickedBtn()
{
	for (int idx = 1; idx <= 50; ++idx) {
		AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
	}
}


void CThreadTestDlg::OnBnClickedPrintBtn()
{
	CString strCount;
	INT_PTR nCount = g_ArrString.GetCount();
	strCount.Format(_T("%d"), nCount);
	MessageBox(strCount);


	for (INT_PTR idx = 0; idx < nCount; ++idx) {
		OutputDebugString(g_ArrString.GetAt(idx));
	}
}



///


①、Mutex(互斥器)


使用方法:
1、创建一个互斥器:CreateMutex;
2、打开一个已经存在的互斥器:OpenMutex;
3、获得互斥器的拥有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放互斥器的拥有权:ReleaseMutex;
5、关闭互斥器:CloseHandle;


HANDLE ghMutex = NULL;
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
	int startIdx = (int)lpParameter;
	for (int idx = startIdx; idx < startIdx+100; ++idx) {
		CString str;
		str.Format(_T("%d"), idx);
		
		DWORD dwWaitResult = WaitForSingleObject(ghMutex, INFINITE);
		switch (dwWaitResult)
		{
		case WAIT_ABANDONED:
		case WAIT_OBJECT_0:
			g_ArrString.Add(str);
			ReleaseMutex(ghMutex);
			break;
		}
		//g_ArrString.Add(str);
	}
	return 0;
}


void CThreadTestDlg::OnBnClickedBtn()
{
	ghMutex = CreateMutex(NULL, FALSE, NULL);
	for (int idx = 1; idx <= 50; ++idx) {
		AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
	}
}


void CThreadTestDlg::OnBnClickedPrintBtn()
{
	CString strCount;
	INT_PTR nCount = g_ArrString.GetCount();
	strCount.Format(_T("%d"), nCount);
	MessageBox(strCount);


	for (INT_PTR idx = 0; idx < nCount; ++idx) {
		OutputDebugString(g_ArrString.GetAt(idx));
	}


	CloseHandle(ghMutex);
}



※ 命名标准:Mutex 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Mutex、Object 等。
最好想一些独一无二的名字等!


固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度会比 Critical Sections 慢几乎100倍的时间(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Mutex 使用正确的情况下不会发生死锁;
5、在“等待”一个 Mutex 的时候,可以指定“结束等待”的时间长度;
6、可以检测到当前拥有互斥器所有权的线程是否已经退出!Wait……函数会返回:WAIT_ABANDONED
===================================================
②、Semaphores(信号量)


租车例子的比喻很恰当!


使用方法:
1、创建一个信号量:CreateSemaphore;
2、打开一个已经存在的信号量:OpenSemaphore;
3、获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放信号量的占有权:ReleaseSemaphore;
5、关闭信号量:CloseHandle;


HANDLE ghSemaphore = NULL;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
	int startIdx = (int)lpParameter;
	CString strOut;
	while(TRUE) {
		
		DWORD dwWaitResult = WaitForSingleObject(ghSemaphore, 0);
		switch (dwWaitResult)
		{
		case WAIT_OBJECT_0:
			strOut.Format(_T("Thread %d: wait succeeded !"), GetCurrentThreadId());
			OutputDebugString(strOut);
			ReleaseSemaphore(ghSemaphore, 1, NULL);


			break;
		case WAIT_TIMEOUT: 
			strOut.Format(_T("Thread %d: wait timed out !"), GetCurrentThreadId());
			OutputDebugString(strOut);
			break; 
		}
	}
	return 0;
}


void CThreadTestDlg::OnBnClickedBtn()
{
	ghSemaphore = CreateSemaphore(NULL, 10, 10, NULL);
	for (int idx = 1; idx <= 20; ++idx) {
		AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
	}
}


void CThreadTestDlg::OnBnClickedPrintBtn()
{
	CloseHandle(ghSemaphore);
}




※ 命名标准:Semaphores 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Semaphore、Object 等



最好想一些独一无二的名字等!


固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Semaphore 使用正确的情况下不会发生死锁;
5、在“等待”一个 信号量 的时候,可以指定“结束等待”的时间长度;
6、非排他性的占有,跟 Critical Sections 和 Mutex 不同,这两种而言是排他性占有,
即:同一时间内只能有单一线程获得目标并拥有操作的权利,而 Semaphores 则不是这样,
同一时间内可以有多个线程获得目标并操作!


所以,这里面问大家一个问题,如果还是用信号量的方式去做之前向 CStringArray 中添加节点的同步可以吗?
===================================================
③、Event Objects(事件)


 Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:
 WaitForSingleObject 一类的函数的调用而改变,所以你可以精确的告诉 Event 对象该做什么事?以及什么时候去做!
 
 使用方法:
1、创建一个事件对象:CreateEvent;
2、打开一个已经存在的事件对象:OpenEvent;
3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
5、手动置为非激发(无信号)状态:ResetEvent
6、关闭事件对象句柄:CloseHandle;


固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、通常被用于 overlapped I/O 或被用来设计某些自定义的同步对象。

#include <windows.h>
#include <stdio.h>


#define THREADCOUNT 4 


HANDLE ghGlobalWriteEvent; 
HANDLE ghReadEvents[THREADCOUNT];


DWORD WINAPI ThreadProc(LPVOID);


void CreateEventsAndThreads(void) 
{
    HANDLE hThread; 
    DWORD i, dwThreadID; 


    // Create a manual-reset event object. The master thread sets 
    // this to nonsignaled when it writes to the shared buffer. 


    ghGlobalWriteEvent = CreateEvent( 
        NULL,               // default security attributes
        TRUE,               // manual-reset event
        TRUE,               // initial state is signaled
        TEXT("WriteEvent")  // object name
        ); 


    if (ghGlobalWriteEvent == NULL) 
    { 
        printf("CreateEvent failed (%d)\n", GetLastError());
        return;
    }
    else if ( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        printf("Named event already exists.\n");
        return;
    }


    // 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 = 0; i < THREADCOUNT; i++) 
    {
        // Create the auto-reset event
        ghReadEvents[i] = CreateEvent( 
            NULL,     // no security attributes
            FALSE,    // auto-reset event
            TRUE,     // initial state is signaled
            NULL);    // object not named


        if (ghReadEvents[i] == NULL) 
        {
            printf("CreateEvent failed (%d)\n", GetLastError());
            return;
        }


        hThread = CreateThread(NULL, 
            0, 
            ThreadProc, 
            &ghReadEvents[i],  // pass event handle
            0, 
            &dwThreadID); 


        if (hThread == NULL) 
        {
            printf("CreateThread failed (%d)\n", GetLastError());
            return;
        }
    }
}


void WriteToBuffer(VOID) 
{
    DWORD dwWaitResult, i; 


    // Reset ghGlobalWriteEvent to nonsignaled, to block readers
 
    if (! ResetEvent(ghGlobalWriteEvent) ) 
    { 
        printf("ResetEvent failed (%d)\n", GetLastError());
        return;
    } 


    // Wait for all reading threads to finish reading


    dwWaitResult = WaitForMultipleObjects( 
        THREADCOUNT,   // number of handles in array
        ghReadEvents,  // 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: 
            // TODO: Write to the shared buffer
            printf("Main thread writing to the shared buffer...\n");
            break;


        // An error occurred
        default: 
            printf("Wait error: %d\n", GetLastError()); 
            ExitProcess(0); 
    } 


    // Set ghGlobalWriteEvent to signaled


    if (! SetEvent(ghGlobalWriteEvent) ) 
    {
        printf("SetEvent failed (%d)\n", GetLastError());
        ExitProcess(0);
    }


    // Set all read events to signaled
    for(i = 0; i < THREADCOUNT; i++) 
        if (! SetEvent(ghReadEvents[i]) ) 
        { 
            printf("SetEvent failed (%d)\n", GetLastError());
            return;
        } 
}


void CloseEvents()
{
    int i;


    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(ghReadEvents[i]);


    CloseHandle(ghGlobalWriteEvent);
}


void main()
{
    int i;


    // TODO: Create the shared buffer


    // Create the events and THREADCOUNT threads to read from the buffer


    CreateEventsAndThreads();


    // Write to the buffer three times, just for test purposes


    for(i=0; i < 3; i++)
        WriteToBuffer();


    // Close the events


    CloseEvents();
}


DWORD WINAPI ThreadProc(LPVOID lpParam) 
{
    DWORD dwWaitResult;
    HANDLE hEvents[2]; 


    hEvents[0] = *(HANDLE*)lpParam;  // thread's read event
    hEvents[1] = ghGlobalWriteEvent; // global write event


    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: 
            // TODO: Read from the shared buffer
            printf("Thread %d reading from buffer...\n", 
                   GetCurrentThreadId());
            break; 


        // An error occurred
        default: 
            printf("Wait error: %d\n", GetLastError()); 
            ExitThread(0); 
    }


    // Set the read event to signaled


    if (! SetEvent(hEvents[0]) ) 
    { 
        printf("SetEvent failed (%d)\n", GetLastError());
        ExitThread(0);
    } 


    return 1;
}



===================================================
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值