GCD实战练习:安全地dispatch_sync任务

“Grand Central Dispatch”中没有下一篇文章了

“Grand Central Dispatch”中没有更新的文章了

有时候,我们不得不将一个任务sync至另一个queue中,但sync操作是一个具有潜在危险的操作:不当的sync会导致进程被锁死。

比如一个queue A,将任务T sync至queue B,而任务T中又将一个子任务 S sync至queue A中,这将必然导致A和B都锁死。

你也许会问,设计任务T时,本身就应该避免其子任务再次sync回A,但在实际工程中,更可能是现象是:任务T的子任务S又调用另外一个任务R,以此类推直至某个任务M,M会将子任务N sync至queue A,这样的深入嵌套往往难以被任务T的开发者考虑到,甚至对任务M全然不知晓。

dispatch_queue_t queueA;
dispatch_queue_t queueB;

void taskR()
{
    printf("this is task R, do you copy?");
}

void taskS()
{
    dispatch_sync(queueA, ^{
        taskR();
    });
}

void taskT()
{
    dispatch_sync(queueB, ^{
        taskS();
    });
}

int foo()
{
    queueA = dispatch_queue_create("queueA", 0);
    queueB = dispatch_queue_create("queueB", 0);
    dispatch_sync(queueA, ^{
        taskT();
    });
    
    dispatch_release(queueA);
    dispatch_release(queueB);
    return 0;
}

所以我们可以编写出一种封装的sync函数(这里以objective-c示例),来保护这种卡死的现象,一旦检测到了卡死,可以打印一些debug消息来警告调用者,在开发环境中可以尽情的使用此函数,而在生产环境中,继续使用此函数也不会有什么问题。

函数如下:

+ (void)scheduleTask01:(void(^)())task targetQueue:(dispatch_queue_t)queue timeOut:(float)seconds
{
    (seconds < 0.0) && (seconds = 0.0);
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_async(queue, ^{
        task();
        dispatch_semaphore_signal(sem);
    });
    
    if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC))) != 0)
    {
        NSLog(@"dispatch timeout sync: time out!");
    }
    dispatch_release(sem);
}

使用async dispatch配合dispatch_semaphore来模拟sync dispatch,使得设置超时时间成为可能。

评论模块尚未加载