我有两种在串行队列上运行的方法。每个方法都返回一些类的副本。我正在尝试实现线程安全解决方案,同时还要保持数据完整性。
例如:
-(Users *) getAllUsers
{
__block copiedUsers;
dispatch_sync(_backgroundQueue, ^{
copiedUsers = [self.users copy]; // return copy object to calling thread.
});
return copiedUsers;
}
-(Orders *) getAllOrders
{
__block copiedOrders;
dispatch_sync(_backgroundQueue, ^{
copiedOrders = [self.Orders copy]; // return copy object to calling thread.
});
return copiedOrders;
}
除了这两种方法之外,我还有一个可以添加/删除用户和订单的工人类,所有这些都是通过串行队列backgroundQueue
完成的。
如果在主线程中我调用getAllUsers
然后getAllOrders
紧接着另一个我的数据完整性是不安全的,因为在两次调用之间,工作者类可能已经更改了模型。
我的问题是如何向调用者提供一个很好的接口,允许多个方法以原子方式运行?
答案 0 :(得分:2)
模型仅从backgroundQueue
串行队列更新。
客户端通过接收在后台队列中运行的块的方法与模型进行对话。
另外,不要冻结主线程,我创建另一个队列并运行一个与网关方法对话的块。
P.S - 注意只在dispatch_sync
中调用runBlockAndGetNeededDataSafely
以避免死锁。
代码示例:
aViewController.m
ManagerClass *m = [ManagerClass new];
dispatch_queue_t q = dispatch_queue_create("funnelQueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block_q = ^{
__Users *users;
__Orders *orders;
[manager runBlockAndGetNeededDataSafely:^
{
users = [manager getUsers];
orders = [manager getOrders];
dispatch_async(dispatch_get_main_queue(),
^{
// got data safely - no thread issues, copied objects. update UI!
[self refreshViewWithUsers:users
orders:orders];
});
}];
}
dispatch_async(q, block_q);
Manager.m实施:
-(void) runBlockInBackground:(dispatch_block_t) block
{
dispatch_sync(self.backgroundQueue, block);
}
-(Users *) getAllUsers
{
return [self.users copy];
}
-(Orders *) getAllOrders
{
return [self.Orders copy];
}
答案 1 :(得分:1)
回答有关如何检查当前队列的问题: 首先,在创建队列时,给它一个标记:
static void* queueTag = &queueTag;
dispatch_queue_t queue = dispatch_queue_create("a queue", 0);
dispatch_queue_set_specific(queue, queueTag, queueTag, NULL);
然后像这样运行一个块:
-(void)runBlock:(void(^)()) block
{
if (dispatch_get_specific(queueTag) != NULL) {
block();
}else {
dispatch_async(self.queue, block);
}
}
答案 2 :(得分:0)
你的例子不起作用。我建议使用完成回调。您应该可以选择知道工人何时完成工作以恢复价值。
- (void)waitForCompletion:(BOOL*)conditions length:(int)len timeOut:(NSInteger)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
BOOL done = YES;
for (int i = 0; i < len; i++) {
done = done & *(conditions+i);
}
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
//update done
done = YES;
for (int i = 0; i < len; i++) {
done = done & *(conditions+i);
}
} while (!done);
}
-(void) getAllUsers:(void(^)(User* user, NSError* error))completion
{
dispatch_async(_backgroundQueue, ^{
BOOL condition[2] = [self.userCondition, self.orderCondition];
[self waitForCompletion: &condition[0] length:2 timeOut:60];
if (completion) {
completion([self.users copy], nil);
}
});
}