use*_*500 7 macos semaphore objective-c grand-central-dispatch ios
我为什么陷入困境?
- (void)foo
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self foo];
});
// whatever...
}
Run Code Online (Sandbox Code Playgroud)
我希望foo在第一次通话时执行两次.
ipm*_*mcc 23
现有的答案都不是很准确(一个是错误的,另一个是有点误导,错过了一些关键细节).首先,让我们直接来源:
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
struct _dispatch_once_waiter_s * volatile *vval =
(struct _dispatch_once_waiter_s**)val;
struct _dispatch_once_waiter_s dow = { NULL, 0 };
struct _dispatch_once_waiter_s *tail, *tmp;
_dispatch_thread_semaphore_t sema;
if (dispatch_atomic_cmpxchg(vval, NULL, &dow)) {
dispatch_atomic_acquire_barrier();
_dispatch_client_callout(ctxt, func);
dispatch_atomic_maximally_synchronizing_barrier();
//dispatch_atomic_release_barrier(); // assumed contained in above
tmp = dispatch_atomic_xchg(vval, DISPATCH_ONCE_DONE);
tail = &dow;
while (tail != tmp) {
while (!tmp->dow_next) {
_dispatch_hardware_pause();
}
sema = tmp->dow_sema;
tmp = (struct _dispatch_once_waiter_s*)tmp->dow_next;
_dispatch_thread_semaphore_signal(sema);
}
} else {
dow.dow_sema = _dispatch_get_thread_semaphore();
for (;;) {
tmp = *vval;
if (tmp == DISPATCH_ONCE_DONE) {
break;
}
dispatch_atomic_store_barrier();
if (dispatch_atomic_cmpxchg(vval, tmp, &dow)) {
dow.dow_next = tmp;
_dispatch_thread_semaphore_wait(dow.dow_sema);
}
}
_dispatch_put_thread_semaphore(dow.dow_sema);
}
}
Run Code Online (Sandbox Code Playgroud)
所以真正发生的是,与其他答案相反,将onceToken其从初始状态更改NULL为第一个调用者堆栈上的地址&dow(调用此调用者1).这在块被调用之前发生.如果在块完成之前有更多的呼叫者到达,则将它们添加到服务器的链接列表中,其中的头部被包含在onceToken该块完成之前(称为呼叫者2..N).在被添加到该列表之后,呼叫者2..N等待呼叫者1的信号量以完成该块的执行,此时呼叫者1将走向链路列表,用信号通知每个呼叫者2..N信号量一次.在那个走的开始,onceToken改变再次成为DISPATCH_ONCE_DONE(它被方便地定义为永远不会是有效指针的值,因此永远不会成为被阻止的调用者的链接列表的头部.)将其更改DISPATCH_ONCE_DONE为使后续调用者便宜的原因(对于其余的调用者来说)过程的生命周期)检查完成状态.
所以在你的情况下,发生的事情是这样的:
-foo,onceToken是nil(由于静态保证被初始化为0保证),并且原子地改变成为服务员链接列表的头部.-foo从块内部递归调用时,您的线程被认为是"第二个调用者",并且存在于这个新的较低堆栈帧中的服务器结构被添加到列表中,然后您将继续等待信号量.所以,简而言之,是的,你已陷入僵局,这里的实际内容是"不要试图递归调用一个dispatch_once块".但问题是,最肯定不是 "无限循环",并且标志是最绝对不是唯一的块执行完毕后改变的-改变其之前的块执行的究竟它是如何知道使呼叫者第2..N等待呼叫者完成.
| 归档时间: |
|
| 查看次数: |
2872 次 |
| 最近记录: |