Objective c Thread/Runloop/Concurrency 考虑

Objective c Thread/Runloop/Concurrency consideration

考虑以下 Class:

@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 内的任务可以 运行.

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

编辑:我对原始问题进行了更改。我从没想过设计的其余部分需要成为问题的一部分。可以在没有最新更改的情况下回答问题,但以防万一。

您似乎假设从两个不同的线程同时调用 -setObject:forKey:-removeObjectForKey: 是安全的,而且绝对 安全.您需要额外的同步。您将 -removeObjectForKey: 调用包装在 -cancelTask: 方法中,并从 -runTask: 方法调用它这一事实是无关紧要的。 NSMutableDictionary 对于来自多个线程的并发操作是不安全的,无论您将其包装在多少其他方法中,如果这些包装方法中的 none 提供任何同步,而此处复制的 none 提供任何同步。