2012-04-26 29 views
59

Tôi cần gửi một khối trên hàng đợi chính, đồng bộ. Tôi không biết liệu tôi hiện có đang chạy trên luồng chính hay không. Giải pháp ngây thơ trông giống như sau:Cách gửi đồng thời trên hàng đợi chính mà không có bế tắc?

dispatch_sync(dispatch_get_main_queue(), block); 

Nhưng nếu hiện tại tôi đang ở trong một khối chạy trên hàng đợi chính, cuộc gọi này sẽ tạo ra bế tắc. (Các công văn đồng bộ chờ khối hoàn thành, nhưng khối thậm chí không bắt đầu chạy, kể từ khi chúng tôi đang chờ đợi hiện tại để kết thúc.)

Bước tiếp theo rõ ràng là kiểm tra hàng đợi hiện tại:

if (dispatch_get_current_queue() == dispatch_get_main_queue()) { 
    block(); 
} else { 
    dispatch_sync(dispatch_get_main_queue(), block); 
} 

Công trình này, nhưng rất xấu. Trước khi tôi ít nhất giấu nó đằng sau một số chức năng tùy chỉnh, không có giải pháp nào tốt hơn cho vấn đề này không? Tôi nhấn mạnh rằng tôi không thể đủ khả năng gửi khối không đồng bộ - ứng dụng đang ở trong tình huống mà khối được gửi không đồng bộ sẽ được thực thi “quá muộn”.

+0

Tôi nghĩ rằng đó là thay vì giải pháp tốt. Tất cả những gì bạn có thể làm là biến nó thành macro được xác định trước, vì vậy mã của bạn sẽ trông không quá xấu xí. –

+1

Đây là giải pháp "sách giáo khoa" nhiều hơn hoặc ít hơn. –

Trả lời

63

tôi cần phải sử dụng một cái gì đó như thế này khá thường xuyên trong Mac và iOS ứng dụng của tôi, vì vậy tôi sử dụng hàm sau helper (ban đầu được mô tả trong this answer):

void runOnMainQueueWithoutDeadlocking(void (^block)(void)) 
{ 
    if ([NSThread isMainThread]) 
    { 
     block(); 
    } 
    else 
    { 
     dispatch_sync(dispatch_get_main_queue(), block); 
    } 
} 

mà bạn gọi qua

runOnMainQueueWithoutDeadlocking(^{ 
    //Do stuff 
}); 

Đây là quá trình bạn mô tả ở trên khá nhiều và tôi đã nói chuyện với một số nhà phát triển khác, những người đã độc lập chế tác một thứ gì đó như thế này cho chính họ.

Tôi đã sử dụng [NSThread isMainThread] thay vì kiểm tra dispatch_get_current_queue(), vì caveats section for that function đã từng cảnh báo việc sử dụng tính năng này để kiểm tra danh tính và the call was deprecated in iOS 6.

+0

Câu trả lời tuyệt vời, còn 'dispatch_get_current_queue()' không được chấp nhận bây giờ – txulu

+1

@Brad_Larson có bao giờ là tình huống thực sự khi một nhà phát triển kiểm soát mã của họ không biết họ có đang phân nhánh từ chủ đề chính hay không? Niềm tin của tôi là nếu ai biết mã của họ đang hoạt động như thế nào thì họ sẽ biết cái gì là/đang/sẽ ở trên sợi chính hay ngược lại. – pnizzle

+2

@pnizzle - Có, có nhiều trường hợp điều này có thể xảy ra.Nếu bạn có một phương thức chung trong đó các thao tác được thực hiện phải chạy trên luồng chính, nhưng được gọi từ nhiều nơi, một số trên luồng chính, một số không, một hàm như thế này cực kỳ hữu ích. Tôi đã viết điều này bởi vì tôi cần nó ở nhiều nơi. –

0

Gần đây tôi đã bắt đầu gặp phải bế tắc trong khi cập nhật giao diện người dùng. Điều đó dẫn tôi đến câu hỏi Stack Overflow này, dẫn đến việc tôi triển khai hàm trợ giúp runOnMainQueueWithoutDeadlocking -type dựa trên câu trả lời được chấp nhận.

Vấn đề thực sự là khi cập nhật giao diện người dùng từ một khối tôi đã sử dụng nhầm dispatch_sync thay vì dispatch_async để nhận hàng đợi Chính cho cập nhật giao diện người dùng. Dễ thực hiện với việc hoàn thành mã và có thể khó nhận thấy sau khi thực tế.

Vì vậy, đối với những người khác đọc câu hỏi này: nếu không thực hiện đồng bộ, chỉ cần sử dụng dispatch_**a**sync sẽ tránh được bế tắc bạn có thể bị liên tục nhấn.

1

Đối với đồng bộ hóa trên hàng đợi chính hoặc trên các chủ đề chính (có nghĩa là không giống nhau) tôi sử dụng:

import Foundation 

private let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1) 
private let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1) 


public func dispatch_sync_on_main_queue(block:() -> Void) 
{ 
    struct dispatchonce { static var token : dispatch_once_t = 0 } 
    dispatch_once(&dispatchonce.token, 
    { 
     dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil) 
    }) 

    if dispatch_get_specific(mainQueueKey) == mainQueueValue 
    { 
     block() 
    } 
    else 
    { 
     dispatch_sync(dispatch_get_main_queue(),block) 
    } 
} 

extension NSThread 
{ 
    public class func runBlockOnMainThread(block:() -> Void) 
    { 
     if NSThread.isMainThread() 
     { 
      block() 
     } 
     else 
     { 
      dispatch_sync(dispatch_get_main_queue(),block) 
     } 
    } 

    public class func runBlockOnMainQueue(block:() -> Void) 
    { 
     dispatch_sync_on_main_queue(block) 
    } 
} 
Các vấn đề liên quan