您的位置:首页 > 编程语言 > C语言/C++

进程、线程同步互斥学习 —— 信号量

2015-06-25 17:15 387 查看
关于信号量,先看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时未进行互斥操作,所以测试结果可能会出现以下结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++