2012-10-01 51 views
2

我試圖從API下載數據,然後將其顯示給用戶。GCD - 具有同步任務的異步隊列

下面是我在做什麼:

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_async(concurrentQueue, ^{ 

    __block NSMutableArray *newImages; 

    dispatch_sync(concurrentQueue, ^{ 
     newImages = [NSMutableArray array]; 
     // images retrieved using `NSURLConnection sendSynchronousRequest` 
    }); 

    dispatch_sync(dispatch_get_main_queue(), ^{ 
     // display images to the user 
    }); 
}); 

我的問題是,既然newImages__block聲明,我總是保證在主隊列執行第二任務時擁有最新的數據newImages?如果不是,將數組內容傳遞到第二個塊並確保其最新內容的最安全方法是什麼?

我認爲我的數據在某個地方變得混亂,因此我在問這個問題。

如果這看起來很好,那麼我會發布我的完整代碼,以幫助整個事情,以防萬一其他錯誤。

回答

2

的溶液的草圖:

q = /* custom serial queue */ 
urls = /* urls array */; 
NSMutableArray *images = [NSMutableArray new]; 
for (NSURL *url in URLs) { 
    NSURLRequest *req = [self imageRequestForURL:url]; 
    dispatch_async(q, ^{ 
    UIImage *image = [self imageFromRequest:req]; 
    [images addObject:newImage]; 
    } 
} 
dispatch_async(q, ^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
    [self applyUpdatesForURLs:urls withImages:images]; 
    }); 
} 

隊列是一個標準的工作隊列。

applyUpdatesForURLs:withImages:塊保證在下載完所有圖像之後運行,這是因爲在圖像下載塊之後被排隊在串行隊列中。

images沒有同步問題,因爲所有使用它的代碼都是串行運行而不是同時運行。

UI更新最終發生在主線程上。

1

您發佈的方法看起來過於複雜。爲什麼不使用NSURLConnection方法,sendAsynchronousRequest:queue:completionHandler :.您可以指定mainQueue作爲隊列參數來更新UI。

NSURL *url = [NSURL URLWithString:@"your server address"]; 
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5]; 
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *theData, NSError *error){ 
     if (error == nil) { 
      //do what you want with theData and update the UI 
     } 
    }]; 
+0

問題是我有多個圖像,我想一次下載它們,然後對其執行UI更新。我正在for循環中抓取圖像。您提供的代碼將通過圖像來完成該圖像。整個批次不會更新更好嗎?我實際上是通過'performBatchUpdates:completion'來更新UI的'UICollectionView',就像你在我以前的問題中看到的那樣。你會建議什麼?我應該用其他東西來替換'performBatchUpdates'嗎?如果是這樣,那麼我將使用什麼來在集合視圖之後執行另一個更新? – darksky

+0

如果不知道下載的詳細信息,我很難分辨出來,但沒有理由我發佈的代碼不能用於批量下載。從服務器下載包含圖像數組(或可以轉換爲數組的一些對象)?如果是這樣,那麼你仍然可以使用performBatchUpdates:completion:來做UI更新。 – rdelmar

1

在併發隊列中不能有多個塊更新可變數組 - 它只是不能正常工作,因爲可變容器不是線程安全的。當圖像可用時,將主隊列上的塊排隊,然後將其添加到陣列中。

+0

儘管兩個塊都是同步分派的。這仍然不安全?如果沒有,當圖像可用時,我如何在主隊列上排列一個塊?你能否詳細說明一下/舉一個例子?我不確定我明白。 – darksky

+0

當您使用dispatch_sync時,它意味着您要等到它完成。如果您有一個運行該塊的併發隊列,則兩個塊都可以嘗試寫入該陣列。當你觸摸數組時,它必須位於像mainQueue這樣的串行隊列上(但如果所有的訪問都在該隊列上,它可以在任何串行隊列上--Mike Ash可能會在這個用法上有文章)。 –

+0

我不確定我的理解。我運行了一個與主線程相關的異步隊列。但是,在該併發隊列中,我運行了兩個同步塊,這應該保證它們將在該隊列中同步運行。放置斷點,我得到了這個行爲,如我所料。第一個塊完成執行,第二個塊執行更新UI。但我相信那裏可能會有一些腐敗的數據。但查詢數組看起來沒問題。 – darksky

1

首先,您不能在併發隊列上使用dispatch_sync()。 (嗯,你可以,但它與dispatch_async()完全一樣。)dispatch_sync()只對串行隊列有任何概念意義,你在這裏說:「我想等到所有塊在我之前排隊,然後做這個塊,然後將控制權返回給調用線程。「

第二,rdelmar's answer是正確的 - 你是過度複雜。即使您不想在NSURLConnection上使用批量完成處理程序,您當然也不需要在併發隊列上嵌套兩個塊分派 - 只需一個批量下載(在併發隊列上運行異步)的塊在完成時在主隊列上進行UI更新的嵌套塊非常好!