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 提供任何同步。
考虑以下 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 提供任何同步。