簡體   English   中英

GCD串行隊列和競爭條件

[英]GCD serial queue and race condition

我有兩種在串行隊列上運行的方法。 每個方法都返回某個類的副本。 我試圖實現線程安全解決方案,同時還要保持數據完整性。

例如:

-(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之后另一方面則是我的數據完整性是安全的,因為兩個電話之間的工人階級可能已經改變了模型。

我的問題是如何為調用方提供一個不錯的接口,該接口允許多個方法自動運行?

僅從backgroundQueue串行隊列更新模型。 客戶端通過一種方法來進行建模,該方法接收在后台隊列中運行的塊。

另外,為了不凍結主線程,我創建了另一個隊列並運行一個與網關方法有關的塊。

PS -注意, 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];
 }

要回答有關如何檢查當前隊列的問題:首先,在創建隊列時,給它添加一個標簽:

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

您的示例不起作用。 我建議使用完成回調。 您應該可以選擇知道工人何時完成工作以恢復價值。

- (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);
        }
    });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM