Tại sao sợi không thể sử dụng nhiều bộ xử lý?


8

Dường như sự khác biệt giữa các sợi và các luồng là các sợi được lên lịch hợp tác, trong khi các luồng được lên lịch trước. Điểm của bộ lập lịch có vẻ như là một cách để làm cho tài nguyên bộ xử lý nối tiếp hoạt động theo cách song song, bằng cách "chia sẻ thời gian" CPU. Tuy nhiên, trên bộ xử lý lõi kép với mỗi lõi chạy luồng riêng, tôi cho rằng không cần phải tạm dừng việc thực thi một luồng này để xử lý tiếp theo vì chúng không "chia sẻ thời gian" cho một bộ xử lý.

Vì vậy, nếu sự khác biệt giữa các luồng và các sợi là cách chúng bị ngắt bởi bộ lập lịch và việc ngắt là không cần thiết khi chạy trên các lõi riêng biệt, tại sao các sợi không thể tận dụng nhiều lõi của bộ xử lý khi các luồng có thể?

Nguồn gây nhầm lẫn:

.. chắc chắn là wikipedia

  1. http://en.wikipedia.org/wiki/Fiber_%28computer_science%29

    Một bất lợi là các sợi không thể sử dụng các máy đa bộ xử lý mà không sử dụng các luồng ưu tiên

  2. http://en.wikipedia.org/wiki/Computer_multitasking#Multithreading

    ... [sợi] có xu hướng mất một số hoặc tất cả lợi ích của các luồng trên các máy có nhiều bộ xử lý.

Câu trả lời:


9

Sự khác biệt chính, như bạn chỉ ra trong câu hỏi của mình, là liệu trình lập lịch biểu có bao giờ ưu tiên một chủ đề hay không. Cách một lập trình viên nghĩ về việc chia sẻ cấu trúc dữ liệu hoặc về việc đồng bộ hóa giữa các "luồng" rất khác nhau trong các hệ thống phòng ngừa và hợp tác.

Trong một hệ thống hợp tác xã (trong đó trôi qua rất nhiều tên, hợp tác đa nhiệm , nonpreemptive đa tác vụ , đề sử dụng cấp , chủ đề màu xanh lá cây , và sợi là năm cái phổ biến hiện nay) các lập trình viên được đảm bảo rằng mã của họ sẽ chạy nguyên tử càng lâu càng họ không thực hiện bất kỳ cuộc gọi hệ thống hoặc cuộc gọi nào yield(). Điều này giúp dễ dàng xử lý các cấu trúc dữ liệu được chia sẻ giữa nhiều sợi. Trừ khi bạn cần thực hiện một cuộc gọi hệ thống như là một phần của phần quan trọng, các phần quan trọng không cần phải được đánh dấu ( ví dụ như với mutex lockunlockcác cuộc gọi). Vì vậy, trong mã như:

x = x + y
y = 2 * x

lập trình viên không cần lo lắng rằng một số sợi khác có thể làm việc với các biến xycùng một lúc. xysẽ được cập nhật cùng nhau nguyên tử từ góc độ của tất cả các sợi khác. Tương tự, tất cả các sợi có thể chia sẻ một số cấu trúc phức tạp hơn, như một cái cây và một cuộc gọi như thế tree.insert(key, value)sẽ không cần phải được bảo vệ bởi bất kỳ phần đột biến hoặc phần quan trọng nào.

Ngược lại, trong một hệ thống đa luồng được ưu tiên, như với các luồng thực sự song song / đa lõi, mọi khả năng xen kẽ các hướng dẫn giữa các luồng đều có thể trừ khi có các phần quan trọng rõ ràng. Một ngắt và ưu tiên có thể trở thành giữa bất kỳ hai hướng dẫn. Trong ví dụ trên:

 thread 0                thread 1
                         < thread 1 could read or modify x or y at this point
 read x
                         < thread 1 could read or modify x or y at this point
 read y
                         < thread 1 could read or modify x or y at this point
 add x and y
                         < thread 1 could read or modify x or y at this point
 write the result back into x
                         < thread 1 could read or modify x or y at this point
 read x
                         < thread 1 could read or modify x or y at this point
 multiply by 2
                         < thread 1 could read or modify x or y at this point
 write the result back into y
                         < thread 1 could read or modify x or y at this point

Vì vậy, để chính xác trên một hệ thống phòng ngừa hoặc trên một hệ thống có các luồng thực sự song song, bạn cần bao quanh mọi phần quan trọng bằng một số loại đồng bộ hóa, như một mutex lockở đầu và một mutex unlockở cuối.

Do đó, các sợi tương tự như các thư viện i / o không đồng bộ hơn là các luồng được ưu tiên hoặc các luồng thực sự song song. Bộ lập lịch sợi được gọi và có thể chuyển đổi các sợi trong các hoạt động i / o có độ trễ dài. Điều này có thể mang lại lợi ích của nhiều hoạt động i / o đồng thời mà không yêu cầu các hoạt động đồng bộ hóa xung quanh các phần quan trọng. Do đó, việc sử dụng các sợi có thể có độ phức tạp lập trình ít hơn các luồng song song hoặc thực sự song song, nhưng việc thiếu đồng bộ hóa xung quanh các phần quan trọng sẽ dẫn đến kết quả thảm hại nếu bạn cố chạy các sợi thực sự đồng thời hoặc phủ đầu.


Tôi nghĩ một số đề cập có lẽ nên được tạo ra từ 1. các hệ thống lai trong đó hệ thống luồng cấp độ người dùng chịu trách nhiệm phân phối (nhiều) luồng cấp độ người dùng trên (vài) lõi CPU và 2. thực tế là khi lập trình trên "kim loại trần" , có thể nhận được đa xử lý mà không cần sự miễn trừ.
dfeuer 14/03/2015

1
@dfeuer Tôi không nghĩ câu hỏi là yêu cầu tất cả các cách khác nhau có thể để tận dụng lợi thế của đa xử lý. Câu hỏi khi tôi đọc là "tại sao các sợi không thể (còn được gọi là các nhiệm vụ không phòng ngừa) được xử lý giống như các chủ đề phủ đầu?" Nếu bạn đang giả sử song song thực sự thì bạn phải đồng bộ hóa chính xác, do đó bạn không còn "sợi".
Logic lang thang

1
Câu trả lời đẹp. Các sợi không thể đảm bảo an toàn vì chương trình sẽ cho rằng nó có quyền truy cập độc quyền vào các tài nguyên được chia sẻ cho đến khi nó chỉ định một điểm ngắt, trong đó các luồng cho rằng một truy cập / đột biến có thể được thực hiện tại bất kỳ điểm nào; rõ ràng là giả định an toàn hơn khi nhiều nút thực sự song song đang tương tác với cùng một dữ liệu.
James M. Lay

6

Câu trả lời thực sự là họ có thể, nhưng có một mong muốn là không.

Sợi được sử dụng bởi vì chúng cho phép bạn kiểm soát cách lập lịch trình xảy ra. Theo đó, việc thiết kế một số thuật toán sử dụng sợi sẽ đơn giản hơn nhiều vì lập trình viên đã nói rằng sợi nào đang được thực thi bất cứ lúc nào. Tuy nhiên, nếu bạn muốn hai sợi được thực thi trên hai lõi khác nhau cùng một lúc, bạn phải tự lên lịch cho chúng để làm như vậy.

Các luồng cho phép kiểm soát mã nào đang được thực thi cho HĐH. Đổi lại, HĐH đảm nhận nhiều nhiệm vụ xấu xí cho bạn. Một số thuật toán trở nên khó khăn hơn, bởi vì lập trình viên ít nói mã nào được thực thi tại một thời điểm nhất định, do đó, nhiều trường hợp bất ngờ hơn có thể xuất hiện. Các công cụ như mutexes và semaphores được thêm vào HĐH để cung cấp cho người lập trình điều khiển vừa đủ để làm cho các chủ đề trở nên hữu ích và đánh bại một số sự không chắc chắn, mà không làm cho lập trình viên sa lầy.

Điều này dẫn đến một thứ thậm chí còn quan trọng hơn cả hợp tác và phòng ngừa: các sợi được điều khiển bởi lập trình viên, trong khi các luồng được điều khiển bởi HĐH.

Bạn không muốn phải sinh ra một sợi trên bộ xử lý khác. Các lệnh mức lắp ráp để làm như vậy rất phức tạp và chúng thường là bộ xử lý cụ thể. Bạn không muốn phải viết 15 phiên bản mã khác nhau để xử lý các bộ xử lý này, vì vậy bạn chuyển sang HĐH. Công việc của HĐH là trừu tượng hóa những khác biệt này. Kết quả là "chủ đề."

Sợi chạy trên đầu của chủ đề. Họ không tự chạy. Theo đó, nếu bạn muốn chạy hai sợi trên các lõi khác nhau, bạn có thể chỉ cần sinh ra hai luồng và chạy một sợi trên mỗi sợi. Trong nhiều triển khai của sợi, bạn có thể làm điều này một cách dễ dàng. Sự hỗ trợ đa lõi không đến từ các sợi, mà là các luồng.

Thật dễ dàng để chỉ ra rằng, trừ khi bạn muốn viết mã cụ thể cho bộ xử lý của riêng mình, bạn không thể làm gì bằng cách gán các sợi cho nhiều lõi mà bạn không thể làm bằng cách tạo các luồng và gán các sợi cho mỗi lõi. Một trong những quy tắc yêu thích của tôi đối với thiết kế API là "API không được thực hiện khi bạn hoàn thành việc thêm mọi thứ vào đó, mà là khi bạn không còn có thể tìm thấy bất cứ điều gì khác để loại bỏ." Do đa lõi được xử lý hoàn hảo bằng cách lưu trữ các sợi trên các luồng, không có lý do gì để làm phức tạp API sợi bằng cách thêm đa lõi ở cấp đó.

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.