天天看點

跨平台調試時,使用Windows的Semaphore模拟Linux下阻塞等候信号量的值為零

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, 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們玩得愉快。

繼續閱讀