跨平台调试时,使用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的代码如下:
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们玩得愉快。
相关文章推荐
- Windows程序调试系列: 使用VC++生成调试信息
- 使用WinDbg和虚拟机调试Windows驱动程序
- Windows 7 下无法使用localhost调试WebApp
- Windows 7 下无法使用localhost调试WebApp
- Windows GUI 程序使用中终端调试
- AWStats: 跨平台的日志分析工具――在GNU/Linux和Windows平台上的使用简介
- Windows下uIP协议栈调试详解(使用winpcap)
- 使用Pix For Windows调试Shader傻瓜教程
- windows 下 信号量使用示例
- Windows 7 环境下使用IE6,web开发调试必备!
- 转载--使用Pix For Windows调试Shader傻瓜教程
- Windows 下使用Eclipse CDT 远程调试 Linux C/C++ 程序
- Windows 下使用Eclipse CDT 远程调试 Linux C/C++ 程序
- 【转帖】使用WinDbg和虚拟机调试Windows驱动程序
- 【转帖】使用WinDbg和虚拟机调试Windows驱动程序
- windows信号量使用
- Windows程序调试系列: 使用VC++生成调试信息
- 2010.11.1 windows下dll的生成、使用、调试
- PHP:windows下使用zend studio及相关产品搭建php调试环境
- 跨平台(unix/linux to windows)ftp传送tar文件时候,必要使用binary模式。