这是一个完整的应用程序示例,它可以支持多个参与者在处理网络请求和用户交互时更改数据。
我为自己设置了一个指南,以便在接触与线程安全相关的任何服务时使用链接到串行 dispatch_queue_t 的 BFExecutor。
指出其他最佳实践会很有用,谢谢!
#import <Bolts/Bolts.h>
dispatch_queue_t serialQueue;
BFExecutor *serialExecutor;
BFTask *maintask;
int32_t key = 0;
BOOL initialized = FALSE;
@implementation yAppDelegate
- (void)setKey:(int32_t)key_ {
// Don't worry about success, just appending to outstanding tasks
maintask = [maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
key = key_;
NSLog(@"set key to %d", key);
[source setResult:nil];
if (initialized) {
[self initialize];
}
return source.task;
}];
}
- (BFTask *)downloadConfig {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
dispatch_async(serialQueue, ^{
NSLog(@"working on init config, key = %d...", key);
// Trigger from a different queue
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[source setResult:nil];
});
});
return source.task;
}
- (BFTask *)initializeService {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
dispatch_async(serialQueue, ^{
NSLog(@"working on init service, key = %d...", key);
// Trigger from a different queue
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[source setResult:nil];
});
// Set final state
initialized = TRUE;
});
return source.task;
}
- (void)initialize {
int32_t oldKey = key;
__block bool reinit = false;
// Start by chaining it to whatever task is in flight without regard to success
// Everything should use the serialQueue or serialExecutor for thread safety
maintask = [[[maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
return [BFTask cancelledTask];
}
return [self downloadConfig];
}] continueWithExecutor:serialExecutor withSuccessBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
return [BFTask cancelledTask];
}
return [self initializeService];
}] continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
}
if (task.error || task.exception || task.isCancelled) {
if (reinit) {
[self initialize];
}
return nil;
} else {
NSLog(@"initService completed = %d", key);
return nil;
}
}];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
serialQueue = dispatch_queue_create("serial", NULL);
serialExecutor = [BFExecutor executorWithDispatchQueue:serialQueue];
// Start with an empty task so all tasks can be strung together without need to initialize
maintask = [BFTask taskWithResult:nil];
// Everything related to changing service state should be contained with the serialQueue dispatch queue
[self setKey:1];
[self initialize];
[self setKey:2];
[self setKey:3];
dispatch_async(dispatch_get_main_queue(), ^(void){
[self setKey:4];
});
dispatch_async(serialQueue, ^(void){
[self setKey:5];
});
[self setKey:6];
// Override point for customization after application launch.
return YES;
}
@end
结果与预期一致。尝试使用 key = 1 进行初始化,但在停止更改之前有所不同。然后使用 key = 5 在其队列中初始化服务,然后使用 key = 4 重新初始化。
结果:
set key to 1
key out of date (0 != 1). reinitializing...
key out of date (0 != 1). reinitializing...
set key to 2
set key to 3
set key to 6
set key to 5
key out of date (1 != 5). reinitializing...
key out of date (1 != 5). reinitializing...
working on init config, key = 5...
working on init service, key = 5...
initService completed = 5
set key to 4
working on init config, key = 4...
working on init service, key = 4...
initService completed = 4