Objective c Thread / Runloop / Concurrency考虑

时间:2016-01-05 02:25:08

标签: objective-c grand-central-dispatch

考虑以下类:

@interface TaskScheduler ()

@property (strong) NSMutableDictionary *tasks;

@end

@implementation TaskScheduler

- (void)addTask:(Task *)task
{
    [_tasks setObject:task forKey:task.id];
}

- (void)cancelTask:(NSString *)id
{
    [_tasks removeObjectForKey:id];
}

- (void)runTask:(Task *)task
{
    // run task in a background concurrent global dispatch queue
    dispatch_queue_t backgroundConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    void (^dispatchBlock)() = ^void(){
        BOOL success = task.taskBlock(); // typedef BOOL (^TaskBlock)();
        if (success)
        {
            [self cancelTask:task.id];
        }
    };
    dispatch_async(backgroundConcurrentQueue, dispatchBlock);
}

- (void)didUpdateSystemUpdateValue
{
    // some other class has a `dispatch_source_t` timer that fires every second and calls this delegate API

    if (shouldRunTask)
    {
        for (Task *task in _tasks.allValues)
        {
            [self runTask:task];
        }
    }
}

@end

现在请注意我如何取消调度队列块调用本身内的任务。

我在这里有点困惑 - runTask:电话中有任何问题吗?如果在使用全局调度队列运行的dispatchBlock内成功,我将取消该任务。只有tasks内的任务才能运行。

唯一的问题我可以看到,如果某些条件成立,同一任务可以多次运行,除非任务在其中一个调度调用中成功,之后它将不会存在于队列中(或任务字典) )。

编辑:我已对原始问题进行了更改。我从未打算将设计的其余部分作为问题的一部分。这个问题可以在没有最新变化的情况下得到解答,但以防万一。

1 个答案:

答案 0 :(得分:3)

您似乎认为从{2}不同的线程同时调用-setObject:forKey:-removeObjectForKey:是安全的,并且它明确地安全。您需要其他同步。您在-removeObjectForKey:方法中包含-cancelTask:调用并从-runTask:方法调用它的事实无关紧要。 NSMutableDictionary对于来自多个线程的并发操作是不安全的,无论你将其包装多少其他方法,如果这些包装方法都没有提供任何同步,这些同步都不会在这里复制。