iPhone - Chủ đề chính của Grand Central


145

Tôi đã sử dụng thành công, công văn trung tâm lớn trong các ứng dụng của mình, nhưng tôi đã tự hỏi đâu là lợi thế thực sự của việc sử dụng thứ gì đó như thế này:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

hoặc thậm chí

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

Ý tôi là, trong cả hai trường hợp, bạn đang bắn một khối sẽ được thực thi trên luồng chính, chính xác là nơi ứng dụng chạy và điều này sẽ không giúp giảm tải. Trong trường hợp đầu tiên, bạn không có bất kỳ kiểm soát nào khi khối sẽ chạy. Tôi đã thấy các trường hợp các khối được thực thi nửa giây sau khi bạn bắn chúng. Trường hợp thứ hai, nó tương tự như

[self doStuff];

đúng?

Tôi tự hỏi các bạn nghĩ gì.


9
Nhân tiện, việc ném một hàng đợi chính vào công văn sẽ dẫn đến bế tắc.
Brooks Hanes

5
Chỉ cần đọc nó trong tài liệu: "Không giống như Clark_async, [Clark_sync] sẽ không quay lại cho đến khi khối kết thúc. Gọi hàm này và nhắm mục tiêu vào hàng đợi hiện tại dẫn đến bế tắc." ... Nhưng có lẽ tôi đang đọc sai ... ( hàng đợi hiện tại không có nghĩa là chủ đề chính). Hãy sửa nếu tôi sai.
Brooks Hanes

4
@BrooksHanes không phải lúc nào cũng đúng. Nó sẽ dẫn đến bế tắc nếu bạn đã ở trên luồng chính. Nếu không thì sẽ không có bế tắc. Xem tại đây
Mật ong

Câu trả lời:


296

Việc gửi một khối tới hàng đợi chính thường được thực hiện từ hàng đợi nền để báo hiệu rằng một số xử lý nền đã kết thúc, vd

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

Trong trường hợp này, chúng tôi đang thực hiện một phép tính dài trên hàng đợi nền và cần cập nhật giao diện người dùng của chúng tôi khi tính toán hoàn tất. Việc cập nhật giao diện người dùng thông thường phải được thực hiện từ hàng đợi chính để chúng tôi 'báo hiệu' trở lại hàng đợi chính bằng cách sử dụng công cụ thứ hai được lồng vào nhau.

Có thể có các ví dụ khác mà bạn có thể muốn gửi trở lại hàng đợi chính nhưng nó thường được thực hiện theo cách này, nghĩa là được lồng từ bên trong một khối được gửi đến hàng đợi nền.

  • xử lý nền xong -> cập nhật giao diện người dùng
  • khối dữ liệu được xử lý trên hàng đợi nền -> tín hiệu hàng đợi chính để bắt đầu đoạn tiếp theo
  • dữ liệu mạng đến trên hàng đợi nền -> tín hiệu hàng đợi chính mà tin nhắn đã đến
  • Vân vân

Về lý do tại sao bạn có thể muốn gửi đến hàng đợi chính từ hàng đợi chính ... Chà, nói chung bạn sẽ không mặc dù bạn có thể làm điều đó để lên lịch một số công việc phải làm trong lần tiếp theo xung quanh vòng chạy.


Ah tôi thấy. Vì vậy, tôi đúng. Không có lợi thế trong việc đó nếu bạn đã ở hàng đợi chính, chỉ khi bạn đang ở hàng đợi khác và muốn cập nhật giao diện người dùng. Cảm ơn.
Vịt

Chỉ cần chỉnh sửa câu trả lời của tôi để nói về lý do tại sao nó không hữu ích để làm điều này từ hàng đợi chính.
Robin Summerhill

Ngoài ra, tôi nghĩ rằng có một lỗi trong iOS 4 (có thể đã xuất hiện trong iOS 5), trong đó việc gửi_số đến hàng đợi chính từ luồng chính chỉ gây ra treo, vì vậy tôi sẽ tránh làm điều đó hoàn toàn.
tham gia

10
Đó không phải là một lỗi, đó là hành vi dự kiến. Hành vi không hữu ích phải thừa nhận nhưng bạn luôn cần phải nhận thức được các bế tắc khi sử dụng Clark_sync. Bạn không thể mong đợi hệ thống sẽ bảo vệ bạn khỏi lỗi lập trình viên mọi lúc.
Robin Summerhill

2
BackgroundQueue ở đây là gì? Làm cách nào để tạo đối tượng backgroundQueue
Nilesh Tupe

16

Việc gửi các khối tới hàng đợi chính từ luồng chính thể hữu ích. Nó cho phép hàng đợi chính có cơ hội xử lý các khối khác đã được xếp hàng để bạn không chỉ đơn giản là chặn mọi thứ khác thực thi.

Ví dụ, bạn có thể viết một máy chủ luồng đơn về cơ bản mà dù sao cũng xử lý nhiều kết nối đồng thời. Miễn là không có khối riêng lẻ nào trong hàng đợi mất quá nhiều thời gian, máy chủ sẽ đáp ứng các yêu cầu mới.

Nếu chương trình của bạn không làm gì ngoài việc dành cả đời để đáp ứng với các sự kiện thì điều này có thể khá tự nhiên. Bạn chỉ cần thiết lập trình xử lý sự kiện của mình để chạy trên hàng đợi chính và sau đó gọi Clark_main (), và bạn có thể không cần phải lo lắng về an toàn luồng.


11

Hy vọng rằng tôi hiểu chính xác câu hỏi của bạn ở chỗ bạn đang tự hỏi về sự khác biệt giữa Clark_async và Clark_sync?

dispatch_async

sẽ gửi khối tới hàng đợi không đồng bộ. Có nghĩa là nó sẽ gửi khối tới hàng đợi và không đợi nó quay trở lại trước khi tiếp tục thực thi mã còn lại trong phương thức của bạn.

dispatch_sync

sẽ gửi khối đến hàng đợi một cách đồng bộ. Điều này sẽ ngăn chặn việc thực thi thêm mã còn lại trong phương thức cho đến khi khối thực hiện xong.

Tôi hầu như đã sử dụng một dispatch_asynchàng đợi nền để thoát khỏi hàng đợi chính và tận dụng mọi lõi bổ sung mà thiết bị có thể có. Sau đó dispatch_asyncđến chủ đề chính nếu tôi cần cập nhật giao diện người dùng.

Chúc may mắn


1
cảm ơn, nhưng tôi đang hỏi về những lợi thế từ việc gửi thứ gì đó đến hàng đợi chính, nằm trên hàng đợi chính.
Vịt

9

Một nơi hữu ích là cho các hoạt động UI, như đặt spinner trước một thao tác dài:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

sẽ không hoạt động, bởi vì bạn đang chặn luồng chính trong suốt thời gian dài và không để UIKit thực sự khởi động spinner.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

sẽ trả lại quyền điều khiển cho vòng lặp chạy, sẽ lên lịch cập nhật giao diện người dùng, bắt đầu công cụ quay vòng, sau đó sẽ đưa điều tiếp theo ra khỏi hàng đợi công văn, đó là quá trình xử lý thực tế của bạn. Khi quá trình xử lý của bạn hoàn tất, lệnh dừng hoạt hình được gọi và bạn quay lại vòng lặp chạy, nơi giao diện người dùng sẽ được cập nhật với điểm dừng.


@Jerceratops có nhưng nó cho phép runloop hiện tại hoàn thành.
Dan Rosenstark

3
Vâng, nhưng nó vẫn khủng khiếp. Nó vẫn chặn UI. Tôi có thể nhấn một nút khác ngay sau nút này. Hoặc thử và cuộn. "(làm điều gì đó dài dòng)" không nên xảy ra trên luồng chính và điều_kết đồng ý để nút bấm "kết thúc" không phải là một giải pháp chấp nhận được.
Jerceratops

8

Swift 3, 4 & 5

Chạy mã trên luồng chính

DispatchQueue.main.async {
    // Your code here
}

1

Async có nghĩa là không đồng bộ và bạn nên sử dụng phần lớn thời gian. Bạn không bao giờ nên gọi đồng bộ hóa trên luồng chính vì nó sẽ khóa UI của bạn cho đến khi tác vụ hoàn thành. Bạn đây là một cách tốt hơn để làm điều này trong Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Nó được bao gồm như là một chức năng tiêu chuẩn trong repo của tôi, hãy kiểm tra xem: https://github.com/goktugyil/EZSwiftExtensions

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.