進程、線程同步互斥學習 —— 信號量

關於信號量,先看MSDN介紹:

Semaphore Objects
A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. The count is decremented each time a thread completes a wait for the semaphore object and incremented each time a thread releases the semaphore. When the count reaches zero, no more threads can successfully wait for the semaphore object state to become signaled. The state of a semaphore is set to signaled when its count is greater than zero, and nonsignaled when its count is zero.The semaphore object is useful in controlling a shared resource that can support a limited number of users. It acts as a gate that limits the number of threads sharing the resource to a specified maximum number.

 For example, an application might place a limit on the number of windows that it creates. It uses a semaphore with a maximum count equal to the window limit, decrementing the count whenever a window is created and incrementing it whenever a window is closed.

即:信號量不同於臨界區,信號量可以限定一定數量線程去執行關鍵代碼,通過P(Wait)、V(Signal)操作來實現對當前可執行關鍵代碼線程的管理。當信號量大於0時爲有信號狀態,等於0爲無信號狀態。線程對信號量操作可以影響信號量中的計數器,通過計數器可以控制線程進入關鍵代碼的數量。


初始化

A thread uses the CreateSemaphore orCreateSemaphoreEx function to create a semaphore object. The creating thread specifies the initial count and the maximum value of the count for the object. The initial count must be neither less than zero nor greater than the maximum value. The creating thread can also specify a name for the semaphore object. Threads in other processes can open a handle to an existing semaphore object by specifying its name in a call to theOpenSemaphore function. 

此處介紹CreateSemaphore和CreateSemaphoreEx

HANDLE WINAPI CreateSemaphore(
  __in_opt  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  __in      LONG lInitialCount,
  __in      LONG lMaximumCount,
  __in_opt  LPCTSTR lpName
);
HANDLE WINAPI CreateSemaphoreEx(
  __in_opt    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  __in        LONG lInitialCount,
  __in        LONG lMaximumCount,
  __in_opt    LPCTSTR lpName,
  __reserved  DWORD dwFlags,
  __in        DWORD dwDesiredAccess
);
lpSemaphoreAttributes SECURITY_ATTRIBUTES  該參數定義了信號量的安全特性。
lInitialCount Long  設置信號量的初始計數。可設置零到lMaximumCount之間的一個值
lMaximumCount Long  設置信號量的最大計數。
lpName String,指定信號量對象的名稱。用vbNullString可創建一個未命名的信號量對象。如果已經存在擁有這個名字的一個信號量,就直接打開現成的信號量。這個名字可能不與一個現有的互斥體、事件、可等待計時器或文件映射的名稱相符。

dwFlags 此參數必須爲0。

dwDesiredAccess  定義信號量的訪問權限。


wait操作

Before a thread attempts to perform the task, it uses theWaitForSingleObject function to determine whether the semaphore's current count permits it to do so. The wait function's time-out parameter is set to zero, so the function returns immediately if the semaphore is in the nonsignaled state. WaitForSingleObject decrements the semaphore's count by one

即:WaitForSingleObject等待信號量dwMilliseconds毫秒,超時或者正常返回都將讓信號量的count 執行減1操作,直到信號量爲0。

DWORD WINAPI WaitForSingleObject(
  __in  HANDLE hHandle,
  __in  DWORD dwMilliseconds
);


signal操作

The ReleaseSemaphore function increases a semaphore's count by a specified amount. The count can never be less than zero or greater than the maximum value.

即:讓信號量的count執行加1操作,但是不會高於MaximumCount.

BOOL WINAPI ReleaseSemaphore(
  __in       HANDLE hSemaphore,
  __in       LONG lReleaseCount,
  __out_opt  LPLONG lpPreviousCount
);
hSemaphore  要操作的信號量對象的句柄,這個句柄必須有SEMAPHORE_MODIFY_STATE 的權限。
lReleaseCount  這個信號量對象在當前基礎上所要增加的值,這個值必須大於0,如果信號量加上這個值會導致信號量的當前值大於信號量創建時指定的最大值,那麼這個信號量的當前值不變,同時這個函數返回FALSE;
lpPreviousCount  指向返回信號量上次值的變量的指針,如果不需要信號量上次的值,那麼這個參數可以設置爲NULL;返回值:如果成功返回TRUE,如果失敗返回FALSE,可以調用GetLastError函數得到詳細出錯信息;


測試代碼:

Lock.h

#pragma once
#include <windows.h>

class ILock
{
public:
	virtual void lock() = 0;
	virtual void unlock() = 0;
};


class CSemaphores : public ILock
{
public:
	CSemaphores(__in LONG lInitialCount,__in LONG lMaximumCount);
	~CSemaphores();

	virtual void lock();
	virtual void unlock();
private:
	HANDLE m_hSemaphore;
};

class CLock
{
public:
	CLock(ILock&);
	~CLock();
private:
	ILock& m_lock;
};
Lock.cpp

#include "stdafx.h"
#include "Lock.h"
#include <assert.h>

CSemaphores::CSemaphores(__in LONG lInitialCount, __in LONG lMaximumCount)
{
	assert(lInitialCount >= 0 && lMaximumCount >= 0 && lInitialCount <= lMaximumCount);
	m_hSemaphore = ::CreateSemaphore(NULL, lInitialCount, lMaximumCount, NULL);

	//失敗
	assert(m_hSemaphore);
}

CSemaphores::~CSemaphores()
{
	CloseHandle(m_hSemaphore);
}

void CSemaphores::lock()
{
	WaitForSingleObject(m_hSemaphore, INFINITE);
}

void CSemaphores::unlock()
{
	::ReleaseSemaphore(m_hSemaphore, 1, NULL);
}

CLock::CLock(ILock& locker) : m_lock(locker)
{
	m_lock.lock();
}

CLock::~CLock()
{
	m_lock.unlock();
}
test.cpp

#include "stdafx.h"
#include <iostream>
#include <process.h>
#include "Lock.h"
#define THREADCOUNT 10
CSemaphores g_semaphore(3,3);
int nFood = 0;

unsigned int WINAPI EatThread(void *pParam)
{
	int i = (int)pParam;
	int nHasEaten = 0;
	while (true)
	{
		//局部對象
		CLock lock(g_semaphore);
		if (nFood > 0 )
		{
			Sleep(100);
			std::cout << "消費者" << i << "進行消費,已經吃掉(" << ++nHasEaten << "),當前剩餘食物" << --nFood << std::endl;
			Sleep(1000);
		}
		else
		{
			break;
		}
	}
	return 0;
}

unsigned int WINAPI ProductThread(void *pParam)
{
	int i = 0;
	while (i < 52)
	{
		std::cout << "生產者進行生產,當前剩餘食物" << ++nFood << std::endl;
		i++;
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hProductThread;
	HANDLE hEatThread[THREADCOUNT];

	hProductThread = (HANDLE)_beginthreadex(NULL, 0, &ProductThread, (void *)0, 0, 0);
	WaitForSingleObject(hProductThread, INFINITE);

	for (int i = 0; i < THREADCOUNT; i++)
	{
		hEatThread[i] = (HANDLE)_beginthreadex(NULL, 0, &EatThread, (void *)i, 0, 0);
	}
	WaitForMultipleObjects(THREADCOUNT, hEatThread, TRUE, INFINITE);

	::CloseHandle(hProductThread);
	for (int i = 0; i < THREADCOUNT; i++)
	{
		::CloseHandle(hEatThread[i]);
	}

	system("pause");
	return 0;
}
此處測試信號量最大爲3,即最多可以有三個線程同時訪問,由於在三線程同時進行訪問nFood時未進行互斥操作,所以測試結果可能會出現以下結果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章