您的位置:首页 > 运维架构 > Linux

跨平台调试时,使用Windows的Semaphore模拟Linux下阻塞等候信号量的值为零

2018-01-11 11:09 399 查看
Linux下的信号量有一个有趣的特性,你可以让信号量阻塞,等候任何一个期望的值。这里,以阻塞等候信号量的值为零说明。

Linux的代码如下:
sem_buf.sem_num = 0;
sem_buf.sem_op  = 0;
sem_buf.sem_flg = 0;

if (semop(mtx, &sem_buf, 1) == -1) {
if (EINTR != errno) {
return -1;
}
} else {
return 0;
}

Windows下先创建进程间通信的Semaphore,例如:
mtx = OpenSemaphore(SEMAPHORE_ALL_ACCESS/*SEMAPHORE_MODIFY_STATE*/, FALSE, TEXT(name));
if (NULL != mtx) {
fprintf(stderr, "Open mutex[%d] %s success! \n", i, name);  
} else {
fprintf(stderr, "Open mutex[%d] %s failed! create... \n", i, name);  
mtx = CreateSemaphore( 
NULL,           // default security attributes
value,
// initial count
20, // maximum count
TEXT(name));    // unnamed semaphore

}

Windows下模拟阻塞等候信号量为零需要一点技巧,MSDN中的如下说明:

It is not possible to reduce the semaphore object count using ReleaseSemaphore, because lReleaseCount cannot be a negative number. To temporarily restrict or reduce access, create a loop in which you call the WaitForSingleObject function with a time-out interval
of zero until the semaphore count has been reduced sufficiently. (Note that other threads can reduce the count while this loop is being executed.) To restore access, call ReleaseSemaphore with the release count equal to the number of times WaitForSingleObject
was called in the loop. 

按MSDN的说明,如下处理,其实不能真正地解决问题,会引起两个进程间执行顺序的错误。
LONG prevCount = 0;
do {
prevCount = 0;
ReleaseSemaphore(mtx, 1, &prevCount); // 这里信号量+1,消费者进程首先抢到CPU运行,引起执行顺序错误
WaitForSingleObject(mtx, INFINITE);
if (0 != prevCount) {
Sleep(50);
}
} while (0 != prevCount);

那么,很自然地,查询Semaphore当前的值,应该可以实现的吧。然而,这样执行ReleaseSemaphore(mtx, 0, &prevCount)会返回错误号7(参数错误),而且prevCount为0,MSDN是这样说的:

lReleaseCount [in] 

The amount by which the semaphore object's current count is to be increased. The value must be greater than zero. If the specified amount would cause the semaphore's count to exceed the maximum count that was specified when the semaphore was created, the count
is not changed and the function returns FALSE.

[吐槽] 实在不知道MS的大牛们设计ReleaseSemaphore函数时怎么想的,这样调用ReleaseSemaphore(handle, 0, &prevCount)时prevCount返回当前的信号量值不是很好么?要不设计另外一个函数可以查询啊!Linuxer们的设计还是要简练实用一些。记得好像libevent源码里面有段注释说MS有些API什么来着的? a shit,真有点儿呢

怎么办呢?设计一个共享内存,保存信号量的引用计数,然后,WaitForSingleObject减1,ReleaseSemaphore加1,模拟阻塞等候信号量的函数中查询共享内存呗。

class refermanager {

public:
typedef struct refer_t_ {
ISEM_KEY
key;
INT32S
refers;
}refer_t;

public:
refermanager() {
HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, name);
int size = MAX_REFERS_ITEMS * sizeof(refer_t);
if (NULL == hMap) {
hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
size,
name);
}
if (NULL != hMap) {
pMemory = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
} else {
pMemory = NULL;
}
}
~refermanager() {
};
static void Set(int key, int value);
static void Inc(int key, int value);
static void Dec(int key, int value);
static int  Get(int key);

protect:
void * pMemory;

}

static refermanager g_manager_obj;

然后, ReleaseSemaphore(mtx, n, &prevCount)之后,执行refermanager::Inc(key, n); WaitForSingleObject之后,执行refermanager::Dec(key,1)。阻塞等候信号量值为零的函数中,如此实现,
for (int n = 1; n > 0; ) {
n = refermanager::Get(key);
if (n > 0) {
Sleep(50);
}
}

更进一步,为了防止两个进程同一时刻访问pMemory,还需要为pMemory加锁保护,用Mutex或者Semaphore都可以,网上有大把的教程,这里就不啰嗦了,祝Windows下的Linuxer们玩得愉快。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  信号量 linux semaphore