小心pthread_cond_signal和SetEvent之间的差异

ZZ FROM:http://blog.csdn.net/absurd/article/details/1402433

=====================================================

转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

作者联系方式:Li XianJing <xianjimli at hotmail dot com>

更新时间:2006-12-19

=====================================================


今天帮同事查一个多线程的BUG,其中一个线程挂在g_cond_wait上不动了。从代码来看,看出不出任何问题,g_cond_waitg_cond_signal是严格配对的。折腾了两个小时后,从LOG信息中发现,g_cond_waitg_cond_signal的顺序有点问题,一个线程先调g_cond_signal,另外一个线程才调g_cond_wait

g_cond_signalglib的封装,在Linux下,是用pthread_cond_signal模拟的,在Win32下,是用SetEvent模拟的。在Win32下,SetEventWaitForSingleObject在两个线程中的调用顺序没有关系,奇怪,难道在linux下两者的调用顺序有影响吗?

看了pthread的代码,果然如此:pthread_cond_signal发现没有其它线程等待,它直接返回了(见用红色高亮的代码)

intpthread_cond_signal(pthread_cond_t *cond)

{

if(cond==NULL)

returnpth_error(EINVAL,EINVAL);

if(*cond== PTHREAD_COND_INITIALIZER)

if(pthread_cond_init(cond,NULL) != OK)

returnerrno;

if(!pth_cond_notify((pth_cond_t *)(*cond),FALSE))

returnerrno;

returnOK;

}

intpth_cond_notify(pth_cond_t *cond,intbroadcast)

{

/* consistency checks */

if(cond==NULL)

returnpth_error(FALSE,EINVAL);

if(!(cond->cn_state & PTH_COND_INITIALIZED))

returnpth_error(FALSE,EDEADLK);

/* do something only if there is at least one waiters (POSIX semantics) */

if (cond->cn_waiters > 0) {

/* signal the condition */

cond->cn_state |= PTH_COND_SIGNALED;

if(broadcast)

cond->cn_state |= PTH_COND_BROADCAST;

else

cond->cn_state &= ~(PTH_COND_BROADCAST);

cond->cn_state &= ~(PTH_COND_HANDLED);

/* and give other threads a chance to awake */

pth_yield(NULL);

}

/* return to caller */

returnTRUE;

}

晚上回家后,我又看了reactos关于SetEvent的实现。结果也意料之中:没有线程等待这个Event时,它仍然会设置SignalState(见用红色高亮的代码)

LONG

STDCALL

KeSetEvent(PKEVENTEvent,

KPRIORITYIncrement,

BOOLEANWait)

{

KIRQLOldIrql;

LONGPreviousState;

PKWAIT_BLOCKWaitBlock;

DPRINT("KeSetEvent(Event %x, Wait %x)/n",Event,Wait);

/* Lock the Dispathcer Database */

OldIrql= KeAcquireDispatcherDatabaseLock();

/* Save the Previous State */

PreviousState=Event->Header.SignalState;

/* Check if we have stuff in the Wait Queue */

if (IsListEmpty(&Event->Header.WaitListHead)) {

/* Set the Event to Signaled */

DPRINT("Empty Wait Queue, Signal the Event/n");

Event->Header.SignalState = 1;

} else {

/* Get the Wait Block */

WaitBlock=CONTAINING_RECORD(Event->Header.WaitListHead.Flink,

KWAIT_BLOCK,

WaitListEntry);

/* Check the type of event */

if(Event->Header.Type== NotificationEvent ||WaitBlock->WaitType == WaitAll) {

if(PreviousState== 0) {

/* We must do a full wait satisfaction */

DPRINT("Notification Event or WaitAll, Wait on the Event and Signal/n");

Event->Header.SignalState = 1;

KiWaitTest(&Event->Header,Increment);

}

}else{

/* We can satisfy wait simply by waking the thread, since our signal state is 0 now */

DPRINT("WaitAny or Sync Event, just unwait the thread/n");

KiAbortWaitThread(WaitBlock->Thread,WaitBlock->WaitKey,Increment);

}

}

/* Check what wait state was requested */

if(Wait==FALSE) {

/* Wait not requested, release Dispatcher Database and return */

KeReleaseDispatcherDatabaseLock(OldIrql);

}else{

/* Return Locked and with a Wait */

KTHREAD *Thread= KeGetCurrentThread();

Thread->WaitNext =TRUE;

Thread->WaitIrql =OldIrql;

}

/* Return the previous State */

DPRINT("Done: %d/n",PreviousState);

returnPreviousState;

}

而在KeWaitForSingleObject中,它发现SignalState大于0,就会Wait成功(见用红色高亮的代码)

NTSTATUS

STDCALL

KeWaitForSingleObject(PVOIDObject,

KWAIT_REASONWaitReason,

KPROCESSOR_MODEWaitMode,

BOOLEANAlertable,

PLARGE_INTEGERTimeout)

{

...

if (CurrentObject->Header.SignalState > 0)

{

/* Another satisfied object */

KiSatisfyNonMutantWait(CurrentObject, CurrentThread);

WaitStatus = STATUS_WAIT_0;

goto DontWait;

}

...

}

由此可见,glib封装的g_cond_signal/g_cond_waitWin32下和Linux下行为并不完全一致。即使不使用glib的封装,自己封装或者直接使用时,也要小心这个微妙的陷阱。

~~end~~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值