考虑以下类:
@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
内的任务才能运行。
唯一的问题我可以看到,如果某些条件成立,同一任务可以多次运行,除非任务在其中一个调度调用中成功,之后它将不会存在于队列中(或任务字典) )。
编辑:我已对原始问题进行了更改。我从未打算将设计的其余部分作为问题的一部分。这个问题可以在没有最新变化的情况下得到解答,但以防万一。答案 0 :(得分:3)
您似乎认为从{2}不同的线程同时调用-setObject:forKey:
和-removeObjectForKey:
是安全的,并且它明确地不安全。您需要其他同步。您在-removeObjectForKey:
方法中包含-cancelTask:
调用并从-runTask:
方法调用它的事实无关紧要。 NSMutableDictionary
对于来自多个线程的并发操作是不安全的,无论你将其包装多少其他方法,如果这些包装方法都没有提供任何同步,这些同步都不会在这里复制。