Hiểu công văn_async


233

Tôi có câu hỏi xung quanh mã này

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Tham số đầu tiên của mã này là

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Có phải chúng ta yêu cầu mã này thực hiện các tác vụ nối tiếp trên hàng đợi toàn cầu với định nghĩa chính là nó trả về hàng đợi đồng thời toàn cầu ở mức ưu tiên nhất định?

Lợi thế của việc sử dụng dispatch_get_global_queuetrên hàng đợi chính là gì?

Tôi bị bối rối. Bạn có thể vui lòng giúp tôi hiểu điều này tốt hơn.


1
Bạn nên cắt mã của bạn thành nhiều dòng để nó có ý nghĩa hơn. an toàn của bạn dispatch_get_global_queuebên trong một loại biến dispatch_queue_t myQueue. Nó dễ đọc hơn khi chỉ chuyển myQueue của bạn tới `` Clark_async`
Alex Cio

Câu trả lời:


517

Lý do chính bạn sử dụng hàng đợi mặc định trên hàng đợi chính là để chạy các tác vụ trong nền.

Ví dụ: nếu tôi đang tải xuống một tệp từ internet và tôi muốn cập nhật người dùng về tiến trình tải xuống, tôi sẽ chạy tải xuống trong hàng đợi mặc định ưu tiên và cập nhật giao diện người dùng trong hàng đợi chính một cách không đồng bộ.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

Tôi hiểu rằng david cảm ơn vì câu trả lời của bạn, nhưng câu hỏi của tôi xoay quanh để hiểu logic của việc này, tức là yêu cầu mã này thực hiện các tác vụ nối tiếp trên hàng đợi toàn cầu, chính là hàng đợi đồng thời
user2332873

Tôi đang làm chính xác những gì bạn đề xuất nhưng bằng cách nào đó, uiTableViewCell không hoạt động ngay khi tôi gọi [self.tableView reloadData] trong Cập nhật giao diện người dùng. Mất khoảng 4 hoặc 5 giây. Nó đã khiến tôi phát điên trong vài ngày nay. .
GrandSteph

@GrandSteph Tôi không quá quen thuộc với phương pháp đó. Có lẽ phương pháp đó chỉ mất 5 giây để chạy. Điều quan trọng với Clark_async là nó cho phép bạn thực hiện mọi thứ trong nền mà không cần treo chủ đề chính.
David

2
những gì hiện các 0nghĩa?
Mật ong

3
@Honey 0 là flagstham số, hiện tại hoàn toàn không có gì. Từ các tài liệu:Flags that are reserved for future use. Always specify 0 for this parameter.
David

199

Tất cả các hàng đợi DISPATCH_QUEUE_PRIORITY_X là các hàng đợi đồng thời (có nghĩa là chúng có thể thực thi nhiều tác vụ cùng một lúc) và là FIFO theo nghĩa các tác vụ trong một hàng đợi nhất định sẽ bắt đầu thực hiện bằng cách sử dụng thứ tự "vào trước, ra trước". Điều này so với hàng đợi chính (từ Clark_get_main_queue ()), là hàng đợi nối tiếp (các tác vụ sẽ bắt đầu thực thi và kết thúc thực hiện theo thứ tự mà chúng được nhận).

Vì vậy, nếu bạn gửi 1000 khối Clark_async () tới DISPATCH_QUEUE_PRIORITY_DEFAULT, các tác vụ đó sẽ bắt đầu thực hiện theo thứ tự bạn đã gửi chúng vào hàng đợi. Tương tự như vậy đối với các hàng đợi CAO, THẤP và BỐI CẢNH. Bất cứ điều gì bạn gửi vào bất kỳ hàng đợi nào trong số này đều được thực hiện trong nền trên các luồng thay thế, cách xa luồng ứng dụng chính của bạn. Do đó, các hàng đợi này phù hợp để thực hiện các tác vụ như tải xuống nền, nén, tính toán, v.v.

Lưu ý rằng thứ tự thực hiện là FIFO trên cơ sở mỗi hàng đợi. Vì vậy, nếu bạn gửi 1000 tác vụ Clark_async () đến bốn hàng đợi đồng thời khác nhau, chia đều và gửi chúng đến BACKGROUND, THẤP, DEFAULT và CAO theo thứ tự (nghĩa là bạn lên lịch 250 tác vụ cuối cùng trên hàng đợi CAO), rất có khả năng đó là các tác vụ đầu tiên bạn thấy bắt đầu sẽ nằm trên hàng đợi CAO đó vì hệ thống đã hàm ý rằng các tác vụ đó cần phải đến CPU càng nhanh càng tốt.

Cũng lưu ý rằng tôi nói "sẽ bắt đầu thực hiện theo thứ tự", nhưng hãy nhớ rằng khi hàng đợi đồng thời, mọi thứ sẽ không nhất thiết phải thực hiện theo thứ tự tùy thuộc vào độ dài thời gian cho mỗi nhiệm vụ.

Theo Apple:

https://developer.apple.com/lvern/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Một hàng đợi gửi đồng thời rất hữu ích khi bạn có nhiều tác vụ có thể chạy song song. Một hàng đợi đồng thời vẫn là một hàng đợi trong đó nó xử lý các nhiệm vụ theo thứ tự nhập trước xuất trước; tuy nhiên, một hàng đợi đồng thời có thể loại bỏ các nhiệm vụ bổ sung trước khi bất kỳ nhiệm vụ nào trước đó kết thúc. Số lượng tác vụ thực tế được thực hiện bởi một hàng đợi đồng thời tại bất kỳ thời điểm nào cũng có thể thay đổi và có thể thay đổi linh hoạt khi các điều kiện trong ứng dụng của bạn thay đổi. Nhiều yếu tố ảnh hưởng đến số lượng tác vụ được thực hiện bởi các hàng đợi đồng thời, bao gồm số lượng lõi có sẵn, số lượng công việc được thực hiện bởi các quy trình khác, số lượng và mức độ ưu tiên của các tác vụ trong các hàng đợi gửi nối tiếp khác.

Về cơ bản, nếu bạn gửi 1000 khối Clark_async () đó đến hàng đợi DEFAULT, CAO, THẤP hoặc BACKGROUND, tất cả chúng sẽ bắt đầu thực hiện theo thứ tự bạn gửi chúng. Tuy nhiên, các nhiệm vụ ngắn hơn có thể hoàn thành trước những nhiệm vụ dài hơn. Lý do đằng sau điều này là nếu có các lõi CPU có sẵn hoặc nếu các tác vụ hàng đợi hiện tại đang thực hiện công việc không chuyên sâu tính toán (do đó làm cho hệ thống nghĩ rằng nó có thể gửi các tác vụ bổ sung song song bất kể số lượng lõi).

Mức độ đồng thời được xử lý hoàn toàn bởi hệ thống và dựa trên tải hệ thống và các yếu tố được xác định bên trong khác. Đây là nét đẹp của Grand Central Dispatch (hệ thống Clark_async ()) - bạn chỉ cần đặt các đơn vị công việc của mình dưới dạng khối mã, đặt mức độ ưu tiên cho chúng (dựa trên hàng đợi bạn chọn) và để hệ thống xử lý phần còn lại.

Vì vậy, để trả lời câu hỏi trên của bạn: bạn đã đúng một phần. Bạn đang "yêu cầu mã đó" để thực hiện các tác vụ đồng thời trên hàng đợi đồng thời toàn cầu ở mức ưu tiên đã chỉ định. Mã trong khối sẽ thực thi trong nền và bất kỳ mã bổ sung (tương tự) nào sẽ thực thi song song tùy thuộc vào đánh giá của hệ thống về các tài nguyên có sẵn.

Mặt khác, hàng đợi "chính" (từ Clark_get_main_queue ()) là một hàng đợi nối tiếp (không đồng thời). Các nhiệm vụ được gửi đến hàng đợi chính sẽ luôn thực hiện theo thứ tự và sẽ luôn hoàn thành theo thứ tự. Các tác vụ này cũng sẽ được thực thi trên Giao diện người dùng để phù hợp để cập nhật giao diện người dùng của bạn với các thông báo tiến trình, thông báo hoàn thành, v.v.


+1, nhưng tôi nghĩ trong thực tế, điều đó không quan trọng lắm cho dù hàng đợi đồng thời là FIFO hay chỉ là thứ tự ngẫu nhiên. Nếu bạn bắt đầu 5 nhiệm vụ trong một vòng lặp, giả sử rằng về cơ bản chúng sẽ bắt đầu cùng một lúc. Không có gì đảm bảo rằng, ví dụ hoạt động I / O đầu tiên của tác vụ 1 sẽ xảy ra trước ngày 5, ngay cả khi chúng thực thi cùng một mã. OTOH, đối với hàng đợi nối tiếp, hành vi FIFO là điều cần thiết và IMHO đây là sự khác biệt xác định giữa hai loại hàng đợi.
Gerhard Wesp

Lời giải thích tuyệt vời. Vỗ tay rất nhiều!
Okhan Okbay

36

Phiên bản Swift

Đây là phiên bản Swift của câu trả lời Objective-C của David. Bạn sử dụng hàng đợi toàn cầu để chạy mọi thứ trong nền và hàng đợi chính để cập nhật giao diện người dùng.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
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.