GCD串行队列和竞争条件

时间:2015-08-06 06:18:10

标签: ios objective-c iphone multithreading

我有两种在串行队列上运行的方法。每个方法都返回一些类的副本。我正在尝试实现线程安全解决方案,同时还要保持数据完整性。

例如:

-(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紧接着另一个我的数据完整性是不安全的,因为在两次调用之间,工作者类可能已经更改了模型。

我的问题是如何向调用者提供一个很好的接口,允许多个方法以原子方式运行?

3 个答案:

答案 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);
        }
    });
}