Sự khác biệt giữa một coroutine trực tiếp và một luồng chủ đề?


Câu trả lời:


122

Coroutines là một hình thức xử lý tuần tự: chỉ có một người đang thực hiện tại bất kỳ thời điểm nào (giống như chương trình con AKA thực hiện các chức năng AKA - chúng chỉ truyền các dùi cui vào nhau một cách trôi chảy hơn).

Các luồng là (ít nhất là về mặt khái niệm) là một hình thức xử lý đồng thời: nhiều luồng có thể được thực thi tại bất kỳ thời điểm nào. (Theo truyền thống, trên các máy đơn CPU, lõi đơn, đồng thời đó được mô phỏng với một số trợ giúp từ HĐH - ngày nay, do có rất nhiều máy là đa CPU và / hoặc đa lõi, nên các luồng sẽ thực hiện đồng thời, không chỉ là "về mặt khái niệm").


187

Lần đọc đầu tiên: Đồng thời so với song song - Sự khác biệt là gì?

Đồng thời là sự phân tách các nhiệm vụ để cung cấp thực thi xen kẽ. Song song là việc thực hiện đồng thời nhiều phần công việc để tăng tốc độ. - https://github.com/servo/servo/wiki/Design

Câu trả lời ngắn: Với các luồng, hệ điều hành chuyển đổi các luồng chạy theo ưu tiên theo lịch trình của nó, đây là một thuật toán trong nhân hệ điều hành. Với coroutines, lập trình viên và ngôn ngữ lập trình xác định khi nào nên chuyển coroutines; nói cách khác, các tác vụ được đa nhiệm hóa bằng cách tạm dừng và tiếp tục các chức năng tại các điểm đã đặt, thường (nhưng không nhất thiết) trong một luồng.

Câu trả lời dài: Trái ngược với các luồng được hệ điều hành lên lịch trước, các công tắc coroutine là hợp tác, nghĩa là lập trình viên (và có thể là ngôn ngữ lập trình và thời gian chạy của nó) điều khiển khi chuyển đổi sẽ xảy ra.

Ngược lại với các chủ đề, được ưu tiên, các công tắc coroutine là hợp tác (lập trình viên điều khiển khi một công tắc sẽ xảy ra). Hạt nhân không tham gia vào các công tắc coroutine. - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

Một ngôn ngữ hỗ trợ các luồng gốc có thể thực thi các luồng của nó (các luồng người dùng) trên các luồng của các hệ điều hành (các luồng nhân ). Mỗi quá trình có ít nhất một luồng nhân. Các luồng nhân giống như các tiến trình, ngoại trừ việc chúng chia sẻ không gian bộ nhớ trong quy trình sở hữu của chúng với tất cả các luồng khác trong quy trình đó. Một quá trình "sở hữu" tất cả các tài nguyên được gán của nó, như bộ nhớ, xử lý tệp, ổ cắm, tay cầm thiết bị, v.v. và các tài nguyên này đều được chia sẻ giữa các luồng nhân của nó.

Bộ lập lịch hệ điều hành là một phần của kernel chạy từng luồng trong một khoảng thời gian nhất định (trên một máy xử lý). Bộ lập lịch phân bổ thời gian (thời gian) cho từng luồng và nếu luồng không kết thúc trong khoảng thời gian đó, bộ lập lịch sẽ tạo trước nó (ngắt nó và chuyển sang luồng khác). Nhiều luồng có thể chạy song song trên một máy đa bộ xử lý, vì mỗi luồng có thể (nhưng không nhất thiết phải được lên lịch) trên một bộ xử lý riêng.

Trên một máy xử lý, các luồng được xử lý nhanh chóng và được ưu tiên (chuyển giữa) (trên Linux, thời gian mặc định là 100ms) khiến chúng đồng thời. Tuy nhiên, chúng không thể chạy song song (đồng thời), vì bộ xử lý lõi đơn chỉ có thể chạy một thứ một lần.

Coroutines và / hoặc máy phát điện có thể được sử dụng để thực hiện các chức năng hợp tác. Thay vì được chạy trên các luồng nhân và được lên lịch bởi hệ điều hành, chúng chạy trong một luồng duy nhất cho đến khi chúng mang lại hoặc kết thúc, mang lại các chức năng khác như được xác định bởi lập trình viên. Các ngôn ngữ với các trình tạo , như Python và ECMAScript 6, có thể được sử dụng để xây dựng các coroutines. Async / await (được thấy trong C #, Python, ECMAscript 7, Rust) là một bản tóm tắt được xây dựng dựa trên các chức năng của trình tạo mang lại tương lai / lời hứa.

Trong một số bối cảnh, coroutines có thể đề cập đến các chức năng xếp chồng trong khi các trình tạo có thể đề cập đến các chức năng không có chồng.

Sợi , sợi nhẹsợi màu xanh lá cây là tên gọi khác của coroutine hoặc những thứ giống như coroutine. Đôi khi chúng có thể trông (thường là về mục đích) giống như các luồng của hệ điều hành trong ngôn ngữ lập trình, nhưng chúng không chạy song song như các luồng thực và thay vào đó hoạt động như coroutines. (Có thể có các đặc tính kỹ thuật cụ thể hoặc khác biệt giữa các khái niệm này tùy thuộc vào ngôn ngữ hoặc cách triển khai.)

Ví dụ, Java có " luồng xanh "; đây là các luồng được máy ảo Java (JVM) lên lịch thay vì tự nhiên trên các luồng nhân của hệ điều hành bên dưới. Chúng không chạy song song hoặc tận dụng nhiều bộ xử lý / lõi - vì điều đó sẽ yêu cầu một luồng gốc! Vì chúng không được HĐH lên lịch, nên chúng giống như coroutines hơn là kernel kernel. Các luồng màu xanh lá cây là những gì Java đã sử dụng cho đến khi các luồng gốc được đưa vào Java 1.2.

Chủ đề tiêu thụ tài nguyên. Trong JVM, mỗi luồng có ngăn xếp riêng, thường có kích thước 1MB. 64k là lượng không gian ngăn xếp ít nhất được phép cho mỗi luồng trong JVM. Kích thước ngăn xếp luồng có thể được cấu hình trên dòng lệnh cho JVM. Mặc dù tên, các luồng không miễn phí, do tài nguyên sử dụng của chúng như mỗi luồng cần ngăn xếp riêng, lưu trữ cục bộ của luồng (nếu có) và chi phí lập lịch trình luồng / chuyển đổi bối cảnh / vô hiệu hóa bộ đệm CPU. Đây là một phần lý do tại sao coroutines trở nên phổ biến cho các ứng dụng quan trọng, có tính đồng thời cao.

Mac OS sẽ chỉ cho phép một quá trình phân bổ khoảng 2000 luồng và Linux phân bổ ngăn xếp 8 MB cho mỗi luồng và sẽ chỉ cho phép nhiều luồng phù hợp với RAM vật lý.

Do đó, các luồng là trọng lượng nặng nhất (về thời lượng sử dụng bộ nhớ và thời gian chuyển ngữ cảnh), sau đó là coroutines và cuối cùng là máy phát điện có trọng lượng nhẹ nhất.


2
+1, nhưng câu trả lời này có thể được hưởng lợi từ một số tài liệu tham khảo.
kojiro

1
Chủ đề màu xanh lá cây là một số điều khác với coroutines. họ không Ngay cả sợi có một số khác biệt. thấy programmers.stackexchange.com/questions/254140/...

112

Chậm trễ khoảng 7 năm, nhưng các câu trả lời ở đây đang thiếu một số bối cảnh trên co-thường vs chủ đề. Tại sao gần đây các coroutines nhận được rất nhiều sự chú ý và khi nào tôi sẽ sử dụng chúng so với các chủ đề ?

Trước hết, nếu coroutines chạy đồng thời (không bao giờ song song ), tại sao mọi người sẽ thích chúng hơn các chủ đề?

Câu trả lời là coroutines có thể cung cấp mức độ đồng thời rất cao với rất ít chi phí . Nói chung trong một môi trường luồng, bạn có tối đa 30-50 luồng trước khi lượng phí bị lãng phí thực sự lên lịch cho các luồng này (bởi bộ lập lịch hệ thống) cắt giảm đáng kể thời gian mà các luồng thực sự làm việc hữu ích.

Ok vậy với các luồng bạn có thể có song song, nhưng không quá nhiều song song, điều đó vẫn tốt hơn so với một đồng quy chạy trong một luồng? Vâng không nhất thiết. Hãy nhớ rằng một đồng quy trình vẫn có thể thực hiện đồng thời mà không cần lập lịch trình - nó chỉ đơn giản là tự quản lý việc chuyển đổi ngữ cảnh.

Ví dụ: nếu bạn có một thói quen thực hiện một số công việc và nó thực hiện một hoạt động mà bạn biết sẽ chặn một thời gian (ví dụ: yêu cầu mạng), với một thói quen chung, bạn có thể ngay lập tức chuyển sang một thói quen khác mà không cần bao gồm cả bộ lập lịch hệ thống trong quyết định này - có bạn, lập trình viên phải xác định khi nào các đồng nghiệp có thể chuyển đổi.

Với rất nhiều thói quen thực hiện các công việc rất nhỏ và tự nguyện chuyển đổi lẫn nhau, bạn đã đạt đến mức hiệu quả mà không một người lập lịch nào có thể hy vọng đạt được. Bây giờ bạn có thể có hàng ngàn coroutine làm việc cùng nhau trái ngược với hàng chục chủ đề.

Vì các thói quen của bạn bây giờ chuyển đổi lẫn nhau một điểm được xác định trước, giờ đây bạn cũng có thể tránh bị khóa trên các cấu trúc dữ liệu được chia sẻ (vì bạn sẽ không bao giờ nói mã của mình chuyển sang một coroutine khác ở giữa phần quan trọng)

Một lợi ích khác là việc sử dụng bộ nhớ thấp hơn nhiều. Với mô hình luồng, mỗi luồng cần phân bổ ngăn xếp riêng của nó và do đó mức sử dụng bộ nhớ của bạn tăng tuyến tính với số lượng luồng bạn có. Với các thói quen chung, số lượng các thói quen bạn không có mối quan hệ trực tiếp với việc sử dụng bộ nhớ của bạn.

Và cuối cùng, các đồng xử lý đang nhận được rất nhiều sự chú ý bởi vì trong một số ngôn ngữ lập trình (như Python), các luồng của bạn không thể chạy song song - chúng chạy đồng thời giống như coroutines, nhưng không có bộ nhớ thấp và chi phí lập lịch miễn phí.


2
Làm thế nào để thực hiện chuyển đổi sang một nhiệm vụ khác trong coroutines khi chúng ta gặp phải một hoạt động chặn?
Narcisse Doudieu Siewe

Cách bạn chuyển sang một tác vụ khác là có bất kỳ thao tác chặn nào thực sự được thực hiện không đồng bộ. Điều này có nghĩa là bạn phải tránh sử dụng bất kỳ hoạt động nào thực sự sẽ chặn và chỉ sử dụng các hoạt động hỗ trợ không chặn khi được sử dụng trong hệ thống coroutine của bạn. Cách duy nhất để giải quyết vấn đề này là có các coroutines được hỗ trợ bởi kernel, chẳng hạn như UMS trên Windows, nơi nó nhảy vào bộ lập lịch của bạn bất cứ khi nào "luồng" UMS của bạn chặn trên một tòa nhà.
retep998

@MartinKonecny ​​Các chủ đề C ++ gần đây TS có tuân thủ cách tiếp cận mà bạn đề cập không?
Nikos

Vì vậy, cuối cùng, một ngôn ngữ lập trình hiện đại sẽ cần cả Coroutines / Fibre để sử dụng hiệu quả một lõi CPU duy nhất cho các hoạt động không tính toán nặng như IO và Chủ đề để song song các hoạt động chuyên sâu của CPU trên nhiều lõi để tăng tốc, phải không?
Mahatma_Firth_Error

19

Trong một từ: sự ưu tiên. Các quân đoàn hành động như những kẻ tung hứng liên tục trao cho nhau một điểm được luyện tập tốt. Chủ đề (chủ đề thực sự) có thể bị gián đoạn tại hầu hết mọi điểm và sau đó được nối lại sau đó. Tất nhiên, điều này mang đến tất cả các loại vấn đề xung đột tài nguyên, do đó GIL khét tiếng của Python - Khóa phiên dịch toàn cầu.

Nhiều triển khai chủ đề thực sự giống như coroutines.


9

Nó phụ thuộc vào ngôn ngữ bạn đang sử dụng. Ví dụ trong Lua chúng là cùng một thứ (loại biến của một coroutine được gọi thread).

Thông thường mặc dù các coroutine thực hiện năng suất tự nguyện trong đó (bạn) lập trình viên quyết định nơi yield, nghĩa là, kiểm soát một thói quen khác.

Thay vào đó, các luồng được quản lý tự động (dừng và khởi động) và chúng thậm chí có thể chạy cùng lúc trên các CPU đa lõi.


0

12 năm muộn để thảo luận nhưng một coroutine có lời giải thích trong tên. Coroutine có thể được phân hủy thành Co và Routine.

Một thường trình trong ngữ cảnh này chỉ là một chuỗi các hoạt động / hành động và bằng cách thực hiện / xử lý một thói quen, chuỗi các hoạt động được thực hiện lần lượt theo thứ tự chính xác như được chỉ định.

Co là viết tắt của hợp tác. Một thói quen chung được yêu cầu (hoặc dự kiến ​​tốt hơn) sẵn sàng đình chỉ việc thực hiện của nó để tạo cơ hội cho các đồng phạm khác thực hiện. Vì vậy, một thói quen chung là về việc chia sẻ tài nguyên CPU (một cách tự nguyện) để những người khác có thể sử dụng cùng một tài nguyên như chính họ đang sử dụng.

Mặt khác, một luồng không cần phải tạm dừng thực hiện. Bị đình chỉ là hoàn toàn trong suốt đối với luồng và luồng bị buộc bởi phần cứng cơ bản phải tạm dừng chính nó. Nó cũng được thực hiện theo cách sao cho nó gần như trong suốt đối với luồng vì nó không được thông báo và trạng thái của nó không bị thay đổi nhưng được lưu và sau đó được khôi phục khi luồng được phép tiếp tục.

Một điều không đúng, đó là các đồng quy không thể được thực hiện đồng thời và các điều kiện chủng tộc không thể xảy ra. Nó phụ thuộc vào hệ thống mà các đồng hoạt động đang chạy và có thể dễ dàng chụp ảnh các thường trình.

Không quan trọng làm thế nào các đồng phạm đình chỉ chính họ. Quay lại Windows 3.1 int 03 được đưa vào bất kỳ chương trình nào (hoặc phải được đặt trong đó) và trong C #, chúng tôi thêm năng suất.

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.