Sự khác biệt giữa DispatchQueue.main.async và DispatchQueue.main.sync


99

Tôi đã sử dụng DispatchQueue.main.asynctrong một thời gian dài để thực hiện các thao tác liên quan đến giao diện người dùng.



Swift cung cấp cả DispatchQueue.main.asyncDispatchQueue.main.syncvà cả hai đều được thực hiện trên hàng đợi chính.



Bất cứ ai có thể cho tôi biết sự khác biệt giữa chúng? Khi nào tôi nên sử dụng mỗi loại?



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}

Câu trả lời:


46

Khi bạn sử dụng asyncnó cho phép hàng đợi cuộc gọi tiếp tục mà không cần đợi cho đến khi khối đã gửi được thực thi. Ngược lại, syncsẽ làm cho hàng đợi gọi dừng lại và đợi cho đến khi công việc bạn đã gửi trong khối được hoàn thành. Do đó synccó thể dẫn đến bế tắc. Hãy thử chạy DispatchQueue.main.synctừ hàng đợi chính và ứng dụng sẽ bị đóng băng vì hàng đợi gọi điện sẽ đợi cho đến khi khối đã gửi kết thúc nhưng nó thậm chí sẽ không thể bắt đầu (vì hàng đợi đang dừng và đang chờ)

Sử dụng khi syncnào? Khi bạn cần đợi một việc gì đó được thực hiện trên hàng đợi KHÁC BIỆT và chỉ sau đó tiếp tục làm việc trên hàng hiện tại của bạn

Ví dụ về sử dụng đồng bộ hóa:

Trên một hàng đợi nối tiếp, bạn có thể sử dụng synclàm mutex để đảm bảo rằng chỉ một luồng có thể thực hiện đoạn mã được bảo vệ cùng một lúc.


Gọi DispatchQueue.main.synctừ một chuỗi nền có sai không?
Mật ong

@Honey Nói chung là không, không có gì sai với một cuộc gọi như vậy (miễn là hàng đợi chính không gây ra bất cứ điều gì nặng nề và tốn thời gian), nhưng trong thực tế, tôi không thể nghĩ ra tình huống mà bạn thực sự cần điều này. Có chắc chắn phải là một giải pháp tốt hơn
Andrey Chernukha

1
@Honey Một tình huống như vậy được cập nhật một CollectionView của PHAssets từ API PhotoKit, như thể hiện trong tài liệu hướng dẫn ở đây: developer.apple.com/documentation/photokit/...
tách trà

1
@teacup thú vị. Tôi chỉ tự hỏi nó sẽ khác như thế nào nếu chúng tôi gọi asyncđến đó? Ý tôi là vì không có gì khác trên chuỗi sau đó nên nó không tạo ra sự khác biệt. Nếu nó là nó DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};thì nó sẽ có ý nghĩa. Nhưng khi không có khối nào khác thì tôi không thể nghĩ đến lợi ích của việc sử dụng DispatchQueue.main.sync {Oneblock}hết DispatchQueue.main.async {Oneblock}. Đối với cả hai, chúng sẽ nhận được ưu tiên mainQueue / ngay lập tức và không có gì có thể làm gián đoạn chúng.
Mật ong,

3
@Honey "vì không có gì khác trên chuỗi sau đó" không đúng khi bạn đang ở trên chuỗi chính, chịu trách nhiệm xử lý tất cả các tương tác của người dùng với ứng dụng. Vì vậy, chẳng hạn, người dùng có thể xóa một ảnh khác trước khi photoLibraryDidChange trả về với nguồn dữ liệu cập nhật gây ra lỗi không nhất quán nghiêm trọng.
tách trà

160

Tại sao lại sử dụng đồng thời?

Ngay sau khi bạn thêm các tác vụ nặng vào ứng dụng của mình như tải dữ liệu, nó sẽ làm chậm giao diện người dùng của bạn hoạt động hoặc thậm chí đóng băng. Đồng thời cho phép bạn thực hiện 2 hoặc nhiều tác vụ “đồng thời”. Nhược điểm của phương pháp này là độ an toàn của luồng không phải lúc nào cũng dễ kiểm soát. Fe khi các tác vụ khác nhau muốn truy cập cùng một tài nguyên như cố gắng thay đổi cùng một biến trên một luồng khác nhau hoặc truy cập tài nguyên đã bị chặn bởi các luồng khác nhau.

Có một vài điểm trừu tượng mà chúng ta cần lưu ý.

  • Hàng đợi.
  • Hiệu suất tác vụ đồng bộ / không đồng bộ.
  • Các ưu tiên.
  • Những rắc rối thường gặp.

Hàng đợi

Phải nối tiếp hoặc đồng thời . Cũng như toàn cầu hoặc riêng tư cùng một lúc.

Với hàng đợi nối tiếp, các tác vụ sẽ được hoàn thành từng cái một trong khi với hàng đợi đồng thời, các tác vụ sẽ được thực hiện đồng thời và sẽ hoàn thành theo lịch trình đột xuất. Cùng một nhóm nhiệm vụ sẽ tốn nhiều thời gian hơn trên hàng đợi nối tiếp so với hàng đợi đồng thời.

Bạn có thể tạo hàng đợi riêng của mình (cả nối tiếp hoặc đồng thời ) hoặc sử dụng hàng đợi toàn cầu (hệ thống) đã có sẵn . Các hàng đợi chính là chỉ đợi nối tiếp ra khỏi tất cả các hàng đợi toàn cầu .

Chúng tôi khuyên bạn không nên thực hiện các tác vụ nặng không liên quan đến công việc giao diện người dùng trên hàng đợi chính (tải dữ liệu từ mạng) mà thay vào đó, hãy thực hiện chúng trên các hàng đợi khác để giữ cho giao diện người dùng không bị đóng băng và phản hồi các hành động của người dùng. Nếu chúng tôi để giao diện người dùng được thay đổi trên các hàng đợi khác, các thay đổi có thể được thực hiện theo một lịch trình và tốc độ khác và bất ngờ. Một số phần tử giao diện người dùng có thể được vẽ trước hoặc sau khi chúng cần thiết. Nó có thể làm hỏng giao diện người dùng. Chúng ta cũng cần lưu ý rằng vì hàng đợi chunghàng đợi hệ thống nên một số tác vụ khác có thể được hệ thống chạy trên chúng.

Chất lượng dịch vụ / Ưu tiên

Hàng đợi cũng có các qos (Chất lượng dịch vụ) khác nhau đặt mức độ ưu tiên thực hiện tác vụ (từ cao nhất đến thấp nhất ở đây):
.userInteractive - hàng đợi chính
.userInitiated - cho các tác vụ do người dùng khởi tạo mà người dùng chờ một số phản hồi
.utility - cho các tác vụ điều này mất một thời gian và không yêu cầu phản hồi ngay lập tức, ví dụ: làm việc với dữ liệu
.background - cho các tác vụ không liên quan đến phần hình ảnh và không nghiêm ngặt về thời gian hoàn thành).

Ngoài ra còn có

.DEFAULT hàng đợi mà does't chuyển nhượng qos thông tin. Nếu không thể phát hiện qos ,qos sẽ được sử dụng giữa .userInitiated.utility .

Các tác vụ có thể được thực hiện đồng bộ hoặc không đồng bộ .

  • Hàm đồng bộ chỉ trả lại quyền điều khiển cho hàng đợi hiện tại sau khi tác vụ kết thúc. Nó chặn hàng đợi và đợi cho đến khi tác vụ kết thúc.

  • Hàm không đồng bộ trả về quyền điều khiển hàng đợi hiện tại ngay sau khi tác vụ được gửi để thực hiện trên hàng đợi khác. Nó không đợi cho đến khi nhiệm vụ kết thúc. Nó không chặn hàng đợi.

Các vấn đề chung.

Những sai lầm phổ biến nhất mà các lập trình viên mắc phải khi chiếu các ứng dụng đồng thời là:

  • Tình trạng cuộc đua - gây ra khi ứng dụng hoạt động phụ thuộc vào thứ tự thực thi các phần mã.
  • Đảo ngược ưu tiên mức độ ưu tiên - khi các tác vụ có mức độ ưu tiên cao hơn chờ các tác vụ có mức độ ưu tiên nhỏ hơn hoàn thành do một số tài nguyên bị chặn
  • Bế tắc - khi một số hàng đợi có thời gian chờ vô hạn cho các nguồn (biến, dữ liệu, v.v.) đã bị chặn bởi một số hàng đợi này.

KHÔNG BAO GIỜ gọi hàm đồng bộ trên hàng đợi chính .
Nếu bạn gọi hàm đồng bộ trên hàng đợi chính, nó sẽ chặn hàng đợi cũng như hàng đợi sẽ chờ tác vụ được hoàn thành nhưng tác vụ sẽ không bao giờ kết thúc vì nó thậm chí sẽ không thể bắt đầu do hàng đợi là đã bị chặn. Nó được gọi là bế tắc .

Khi nào sử dụng đồng bộ hóa? Khi chúng ta cần đợi cho đến khi nhiệm vụ kết thúc. Fe khi chúng ta đảm bảo rằng một số hàm / phương thức không được gọi kép. Fe chúng tôi đã đồng bộ hóa và cố gắng ngăn nó được gọi gấp đôi cho đến khi hoàn tất. Dưới đây là một số mã cho mối quan tâm này:
Làm thế nào để tìm ra nguyên nhân gây ra lỗi báo cáo sự cố trên thiết bị IOS?


3
Tôi không nghĩ rằng "KHÔNG BAO GIỜ gọi hàm đồng bộ trên hàng đợi chính" là đúng. Có những trường hợp khi bạn gọi đồng bộ hóa trong chuỗi chính, chẳng hạn như khi bạn có một bộ đếm toàn cục mà bạn cần từng đối tượng để sử dụng và tăng: dispQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold

6
Lớp QOS - .userInteractive KHÔNG phải là hàng đợi chính.
Kunal Shah

1
Có sai nếu gọi DispatchQueue.main.synctừ một chuỗi nền không?
Mật ong

1
@Honey, không có gì là sai khi gọi như vậy nhưng theo kinh nghiệm của tôi, bạn sẽ thấy mình gọi DispatchQueue.main.async nhiều hơn ngoài đồng bộ hóa.
James Kim

2
Sẽ không chính xác hơn khi nói rằng bạn không bao giờ nên gọi hàm sync () trên hàng đợi hiện tại? Không sai khi gọi đồng bộ hóa () trên hàng đợi chính nếu bạn đang ở hàng đợi khác, nếu tôi hiểu đúng.
ykay

0

synchoặc asynccác phương thức không ảnh hưởng đến hàng đợi mà chúng được gọi.

syncsẽ chặn các chủ đề từ đó nó được gọi là và không phải là hàng đợi trên đó nó được gọi. Nó là thuộc tính DispatchQueuequyết định liệu DispatchQueuesẽ đợi thực thi tác vụ (hàng đợi nối tiếp) hay có thể chạy tác vụ tiếp theo trước khi tác vụ hiện tại hoàn thành (hàng đợi đồng thời).

Vì vậy, ngay cả khi DispatchQueue.main.asynclà một cuộc gọi không đồng bộ, một hoạt động tác vụ nặng được thêm vào nó có thể đóng băng giao diện người dùng vì các hoạt động của nó được thực thi nối tiếp trên luồng chính. Nếu phương thức này được gọi từ luồng nền, điều khiển sẽ trở lại luồng đó ngay lập tức ngay cả khi giao diện người dùng dường như bị đóng băng. Điều này là do asynccuộc gọi được thực hiện vàoDispatchQueue.main


0

GCDcho phép bạn thực hiện một nhiệm vụ synchronouslyhoặc asynchronously[Giới thiệu] [Thêm]

synchronous(khối và chờ) hàm trả về một điều khiển khi nhiệm vụ sẽ được hoàn thành

asynchronousHàm (điều khiển và tiến hành) trả về một điều khiển ngay lập tức, điều khiển nhiệm vụ bắt đầu đến một hàng đợi thích hợp nhưng không đợi nó hoàn thành.

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.