dispatch_semaphore_t重用-我在这里缺少什么? [英] dispatch_semaphore_t reuse - What am I missing here?
问题描述
我有一些代码,我正在使用dispatch_semaphore_t表示操作完成.当信号量是成员变量时,它似乎无法正常运行.我将显示有效的示例代码和似乎无效的示例:
I have some code where I am using dispatch_semaphore_t to signal operation completion. When the semaphore is a member variable, it does not seem to behave correctly. I will show example code that works and an example that does not seem to work:
@implementation someClass
{
dispatch_semaphore_t memberSem;
dispatch_semaphore_t* semPtr;
NSThread* worker;
BOOL taskDone;
}
- (id)init
{
// Set up the worker thread and launch it - not shown here.
memberSem= dispatch_semaphore_create(0);
semPtr= NULL;
taskDone= FALSE;
}
- (void)dealloc
{
// Clean up the worker thread as needed - not shown here.
if((NULL != semPtr) && (NULL != *semPtr))
disptatch_release(*semPtr);
dispatch_release(memberSem);
}
- (void)doSomethingArduous
{
while([self notDone]) // Does something like check a limit.
[self doIt]; // Does something like process data and increment a counter.
taskDone= TRUE; // I know this should be protected, but keeping the example simple for now.
if((NULL != semPtr) && (NULL != *semPtr))
dispatch_semaphore_signal(*semPtr); // I will put a breakpoint here, call it "SIGNAL"
}
- (BOOL)getSomethingDoneUseLocalSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
dispatch_semaphore_t localSem= dispatch_semaphore_create(0);
semPtr= &localSem;
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(localSem, timeUp);
semPtr= NULL;
dispatch_release(localSem);
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE;
return FALSE;
}
- (BOOL)getSomethingDoneUseMemberSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
semPtr= &memberSem; // I will put a breakpoint here, call it "START"
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(memberSem, timeUp);
semPtr= NULL;
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE; // I will put a breakpoint here, call it "TASK_DONE"
return FALSE; // I will put a breakpoint here, call it "TASK_NOT_DONE"
}
- (void)hereIsWhereWeBringItTogether
{
BOOL gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
BOOL gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return TRUE. I will put a breakpoint here, call it "RUN_TEST"
gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return FALSE.
}
因此,考虑到代码和得到/得到的结果,我按照实际代码中的描述放置了断点:一个在主函数中,一个在工作函数中开始,一个在信号量上指示成员信号量,以及等了两个.
So, given that code and the results I get/got, I put the breakpoints as described in my real code: One in the main function, one to start in the work function, one where the member semaphore is signaled, and two after the wait.
我发现的是在使用成员信号量的情况下,在第一轮中,我在断点"RUN_TEST"处停止运行,并击中断点"START",先运行然后击中断点"SIGNAL",然后运行并击中断点"TASK_DONE"-全部符合预期.
What I found was in the case where I use the member semaphore, in the first round I stop at breakpoint "RUN_TEST", run and hit breakpoint "START", run then hit breakpoint "SIGNAL", run then hit breakpoint "TASK_DONE" - all as expected.
当我继续运行时,我击中了断点"START",然后又击中了断点"TASK_NOT_DONE",然后又击中了断点"SIGNAL"
When I continue to run, I hit breakpoint "START", run then hit breakpoint "TASK_NOT_DONE", run then hit breakpoint "SIGNAL"
也就是说,当我使用属于成员的信号量运行序列并执行看起来正确的信号/等待时,第二次尝试等待该信号量时,我似乎不知所措,并且在我收到信号后已经退出等待状态.
That is, when I run the sequence using a semaphore that is a member, and do what looks like proper signal/wait, the second time I try to wait on that semaphore I seem to blow by and it gets signaled after I have exited the wait.
我似乎要么没有管理计数权(信号/等待配对),要么成员信号量不会回到未信号状态.
I seem to either be not managing the counting right (signal/wait pairings) or that member semaphore will not go back to an un-signaled state.
我的感觉是我在这里缺少一些基本的东西.任何输入将不胜感激.
My feeling is there is something fundamental I am missing here. Any input would be appreciated.
最终,我似乎缺少的是由于我的实际代码更加复杂.涉及到多个线程和一个postNotification,而不是从繁重的任务中获得明确的回报.我用通知处理程序中的代码替换了postNotification-它设置了一个标志并向信号灯发出信号.这样,通知处理程序可能会引入任何延迟.
Ultimately what I seemed to be missing was due to my actual code being a bit more complicated. Instead of a clean return from the arduous task, there are multiple threads involved and a postNotification. I replaced the postNotification with the code in the notification handler - it sets a flag and signals the semaphore. That way any delay that might have been introduced by the notification handler is eliminated.
推荐答案
是的,这是预期的行为.如果您在等待信号时超时,则在信号到达时,该信号量的 next 调用将捕获该信号.考虑以下示例:
Yes, this is the expected behavior. If you time out waiting for a signal, when the signal comes it, it will be caught by the next call to dispatch_semaphore_wait
for that particular semaphore. Consider the following example:
例如:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t timeout;
// in 5 seconds, issue signal
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
NSLog(@"Signal 1");
dispatch_semaphore_signal(semaphore);
});
// wait four seconds for signal (i.e. we're going to time out before the signal)
NSLog(@"Waiting 1");
timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
if (dispatch_semaphore_wait(semaphore, timeout))
NSLog(@"Waiting for 1: timed out");
else
NSLog(@"Waiting for 1: caught signal");
// now, let's issue a second signal in another five seconds
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
NSLog(@"Signal 2");
dispatch_semaphore_signal(semaphore);
});
// wait another four seconds for signal
// this time we're not going to time out waiting for the second signal,
// because we'll actually catch that first signal, "signal 1")
NSLog(@"Waiting 2");
timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC));
if (dispatch_semaphore_wait(semaphore, timeout))
NSLog(@"Waiting for 2: timed out");
else
NSLog(@"Waiting for 2: caught signal");
// note, "signal 2" is still forthcoming and the above code's
// signals and waits are unbalanced
因此,当您使用类实例变量时,您的getSomethingDoneUseMemberSemaphore
的行为如上所述,其中对dispatch_semaphore_wait
的第二次调用将捕获发出的第一个信号,因为(a)它是相同的信号量; (b)如果第一次拨打dispatch_semaphore_signal
的电话超时了.
So, when you use the class instance variable, your getSomethingDoneUseMemberSemaphore
behaves like above, where the second call to dispatch_semaphore_wait
will catch the first signal issued because (a) it's the same semaphore; and (b) if the first call to dispatch_semaphore_signal
timed out.
但是,如果您每次都使用唯一的信号量,那么对dispatch_semaphore_wait
的第二次调用将不会响应第一个信号量的dispatch_semaphore_signal
.
But if you use unique semaphores each time, then the second call to dispatch_semaphore_wait
will not respond to the dispatch_semaphore_signal
of the first semaphore.
这篇关于dispatch_semaphore_t重用-我在这里缺少什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!