Những gì cấu thành sử dụng đúng chủ đề trong lập trình?


13

Tôi mệt mỏi khi nghe mọi người khuyên bạn chỉ nên sử dụng một luồng cho mỗi bộ xử lý, trong khi nhiều chương trình sử dụng tối đa 100 cho mỗi quy trình! lấy ví dụ một số chương trình phổ biến

vb.net ide uses about 25 thread when not debugging
System uses about 100
chrome uses about 19
Avira uses more than about 50

Bất cứ khi nào tôi đăng một câu hỏi liên quan đến chủ đề, tôi đều nhắc nhở rằng mỗi lần tôi không nên sử dụng nhiều hơn một luồng cho mỗi bộ xử lý và tất cả các chương trình tôi đề cập ở trên đều làm hỏng hệ thống của tôi với một bộ xử lý.


7
Đề nghị đó là rộng. Giới hạn của một luồng trên mỗi bộ xử lý chỉ phù hợp cho các ứng dụng bị ràng buộc tính toán. Hầu hết các chương trình đều bị ràng buộc IO, cho dù đó là lưu lượng mạng, truy cập đĩa hoặc thậm chí RAM. Đó là lý do tại sao các máy chủ web, cơ sở dữ liệu, vv có nhóm luồng với nhiều luồng hơn lõi xử lý.
Kilian Foth

2
"Tôi được nhắc nhở gần như mọi lúc rằng tôi không nên sử dụng nhiều hơn một luồng cho mỗi bộ xử lý"? Bạn có thể gửi liên kết hoặc ví dụ? Hầu như mọi lúc?
S.Lott

2
"... mọi người khuyên bạn chỉ nên sử dụng một luồng cho mỗi quy trình." Những người này là ai? Lập kế hoạch đã tiến bộ đáng kể kể từ thời kỳ đen tối.
Rein Henrichs

2
Bạn không nên có nhiều hơn một luồng UI cho mỗi tiến trình.
SLaks

3
@Billy ONeal, bản chỉnh sửa của bạn khiến câu hỏi trở nên vô nghĩa
SK-logic

Câu trả lời:


22

bạn chỉ nên sử dụng một luồng cho mỗi bộ xử lý,

Có thể trong HPC nơi bạn muốn có hiệu quả tối đa - nhưng nếu không thì điều ngu ngốc nhất tôi đã nghe thấy ngày hôm nay!

Bạn nên sử dụng số lượng chủ đề phù hợp với thiết kế của chương trình và vẫn cho hiệu suất chấp nhận được.

Đối với một máy chủ web, có thể hợp lý để thực hiện một luồng cho mỗi kết nối đến (mặc dù có nhiều cách tốt hơn cho các máy chủ được tải rất nhiều).

Đối với một ide, mỗi công cụ chạy trong luồng riêng của nó không phải là không hợp lý. Tôi nghi ngờ nhiều luồng được báo cáo cho .Net IDE là những thứ như ghi nhật ký và các tác vụ I / O được bắt đầu trong các luồng của chúng để chúng có thể tiếp tục được bỏ chặn.


9
Bây giờ bạn đã khiến tôi tự hỏi điều ngu ngốc nhất bạn từng nghe là gì!
Michael K

3
@Michael - Tôi đã dạy những sinh viên chưa tốt nghiệp và làm việc trong các hợp đồng quốc phòng - bạn sẽ không tin vào những điều ngu ngốc nhất tôi từng nghe!
Martin Beckett

1
Chúng tôi đã thấy chúng trên TheD DailyWTF.com chưa?
Thất vọngWithFormsDesigner

Tôi thực sự không thể tìm thấy chúng ngay bây giờ, nhưng hãy nhìn vào liên kết này social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/ Lỗi
Smith

2
tối đa một luồng liên kết CPU trên mỗi bộ xử lý được phân bổ cho ứng dụng. Các luồng liên kết với IO không phải là một vấn đề lớn (ngoài bộ nhớ mà chúng tiêu thụ) và điều quan trọng cần nhớ là các ứng dụng có thể bị hạn chế chỉ sử dụng một tập hợp con CPU của hệ thống; xét cho cùng, đó là (thường) máy tính của người dùng / quản trị viên chứ không phải máy tính của lập trình viên.
Donal Fellows

2

Lời khuyên một luồng cho mỗi lõi áp dụng khi mục đích là tốc độ thông qua thực thi song song.

Một lý do hoàn toàn khác và có giá trị như nhau là sự đơn giản của mã khi nó phải đáp ứng với các sự kiện không thể đoán trước. Vì vậy, nếu một chương trình phải nghe trên 100 ổ cắm và xuất hiện để chú ý đầy đủ đến từng ổ cắm, thì đó là một cách sử dụng hoàn hảo để xâu chuỗi. Một ví dụ khác là UI, trong đó một luồng xử lý các sự kiện UI, trong khi một luồng khác xử lý nền.


1
Xử lý ràng buộc IO có thể được thực hiện dưới dạng một luồng trên mỗi nguồn sự kiện hoặc nhiều nguồn sự kiện có thể được ghép vào một luồng. Mã đa kênh thường phức tạp hơn và hiệu quả hơn.
Donal Fellows

2

Bạn muốn một luồng cho mỗi phép tính có thể tiến hành ở các mức khác nhau so với các phép tính khác.

Đối với tính toán liên kết với CPU song song, có khối lượng công việc lớn, bạn thường muốn có một luồng cho mỗi CPU, vì một khi tất cả đều bận rộn, nhiều luồng không giúp đỡ và chỉ tạo ra chi phí lập lịch. Nếu các khối công việc có kích thước không đều theo thời gian hoặc được tạo động khi chạy (thường xảy ra khi bạn có cấu trúc dữ liệu phức tạp lớn để xử lý), bạn có thể muốn gắn các khối đó vào nhiều luồng, do đó, bộ lập lịch luôn có số lượng lớn được thiết lập để chọn khi một số khối công việc hoàn thành, để giữ cho tất cả các CPU luôn bận rộn.

Đối với tính toán ràng buộc I / O, bạn thường muốn có một luồng cho mỗi "kênh" I / O độc lập vì chúng giao tiếp ở các tốc độ khác nhau và các luồng bị chặn trên kênh sau đó không ngăn các luồng khác tiến triển.


Chỉ cần lưu ý rằng phong cách phân luồng này có thể dẫn đến một số chương trình được kiến ​​trúc kỳ quặc. Tôi đã thấy một chương trình 4 luồng có một luồng để đọc các bản ghi từ bảng DB, một luồng để ghi các bản ghi được chuyển đổi vào một ổ cắm, một luồng để đọc các câu trả lời cho các ghi đó của ổ cắm (đã bị lỗi và không đồng bộ) và một luồng để sửa đổi bản ghi DB gốc với câu trả lời. Điều kiện lỗi không trực quan xảy ra sau đó.
Bruce Ediger

Một quan điểm là phong cách này tạo ra các chương trình kỳ lạ. Một quan điểm khác là phong cách tự nhiên mà các chương trình nên có. Dunno về các điều kiện lỗi "không trực quan"; nếu bạn có nhiều điều xảy ra và một trong số chúng bị lỗi, đảm bảo rằng nó được truyền đúng theo các tính toán không đồng bộ là một vấn đề đối với nhiều langau [một cách ngu ngốc, các ngoại lệ Java không được xác định tại các ranh giới luồng], nhưng không phải một vấn đề với phong cách chương trình. (Ngôn ngữ lập trình PARLANSE của chúng tôi [xem tiểu sử của tôi] xử lý các trường hợp ngoại lệ trên các ranh giới luồng một cách sạch sẽ để có thể thực hiện điều này đúng.).
Ira Baxter

1

Nguyên tắc chung cho các luồng là, bạn muốn có ít nhất một luồng công việc "hoạt động" (có thể thực hiện các lệnh của nó ngay lập tức trong thời gian CPU) cho mỗi "đơn vị thực thi" có sẵn trên máy tính. "Đơn vị thực thi" là một bộ xử lý hướng dẫn logic, do đó, một máy chủ siêu phân luồng Xeon bốn nhân, bốn lõi sẽ có 32 EU (4 chip, 4 lõi cho mỗi chip, mỗi siêu phân luồng). Core i7 trung bình của bạn sẽ có 8.

Một luồng trên mỗi EU là việc sử dụng toàn bộ sức mạnh của CPU, với điều kiện là các luồng sẽ luôn ở trạng thái hoạt động; điều này gần như không bao giờ xảy ra, vì các luồng cần truy cập vào bộ nhớ không được lưu trong bộ nhớ cache, đĩa cứng, cổng mạng, v.v. mà chúng phải chờ và điều đó không đòi hỏi phải có sự chú ý của CPU hoạt động. Do đó, bạn có thể tăng thêm hiệu quả tổng thể với nhiều chủ đề được xếp hàng và hiếm khi đi. Điều này không đi kèm với chi phí; khi CPU chuyển đổi một luồng, nó phải lưu trữ các thanh ghi, con trỏ thực thi và thông tin trạng thái khác thường được lưu trong các hoạt động bên trong của EU và truy cập rất nhanh, cho phép các EU khác trong chip CPU đó nhận nó. Nó cũng yêu cầu các luồng trong HĐH để quyết định nên chuyển sang luồng nào. Cuối cùng, khi một EU chuyển chủ đề, nó làm mất hiệu suất của đường ống mà hầu hết các kiến ​​trúc bộ xử lý sử dụng; nó phải xả đường ống trước khi chuyển chủ đề. Nhưng, vì tất cả điều này vẫn mất trung bình ít thời gian hơn nhiều so với việc chờ đợi ổ cứng hoặc thậm chí RAM trở lại với thông tin, nó đáng giá.

Tuy nhiên, nói chung, một khi bạn vượt quá số lượng chủ đề "hoạt động" gấp đôi so với EU, HĐH bắt đầu dành nhiều thời gian lên lịch cho các chủ đề của EU và EU dành nhiều thời gian hơn để chuyển đổi giữa chúng, hơn là thực sự dành cho việc chạy các chủ đề đang hoạt động của các chương trình. Đây là điểm không kinh tế của quy mô; thực sự sẽ mất nhiều thời gian hơn để thuật toán đa luồng chạy nếu bạn thêm một luồng bổ sung vào thời điểm này.

Vì vậy, về tổng thể, bạn muốn duy trì ít nhất nhiều luồng trong chương trình của mình khi bạn có EU trên máy tính, nhưng bạn muốn tránh có nhiều hơn gấp đôi số đó không chờ hoặc ngủ.


Nếu N là số lượng chủ đề và U số lượng đơn vị, thì OP đã đặt câu hỏi cho quy tắc "N = U". Bạn đang thư giãn theo quy tắc "U <= N <= 2 U". Tôi sẽ đi xa hơn một chút và nói rằng "N <= c U" cho hằng số "nhỏ hợp lý" (được lập trình viên biết) c có thể chấp nhận được (nếu điểm chuẩn cho thấy hiệu suất hợp lý). Tôi sẽ rất lo lắng nếu số lượng chủ đề có thể tăng lên một số lượng không giới hạn.
5gon12eder

1

Bạn nên sử dụng một chủ đề cho:

Mỗi bộ xử lý bạn cần phải bận rộn.

Mỗi I / O bạn có thể sử dụng đồng thời một cách hữu ích mà bạn không thể thực hiện theo cách không chặn. (Ví dụ: đọc từ đĩa cục bộ.)

Mỗi tác vụ yêu cầu một luồng chuyên dụng, ví dụ như gọi vào thư viện không có giao diện không chặn hoặc nơi giao diện không chặn không phù hợp. Điều này bao gồm các nhiệm vụ như giám sát đồng hồ hệ thống, bộ hẹn giờ bắn, v.v.

Một vài bổ sung để bảo vệ chống lại sự chặn bất ngờ như lỗi trang.

Một vài bổ sung để bảo vệ chống lại việc chặn dự kiến ​​không đáng để tối ưu hóa, ví dụ như trong mã không quan trọng. .

Nếu bạn tuân theo quy tắc "một luồng trên mỗi bộ xử lý", thì tất cả mã của bạn là hiệu suất quan trọng. Bất kỳ mã nào chặn vì một số lý do có nghĩa là quá trình của bạn không thể sử dụng bộ xử lý đó. Điều đó làm cho lập trình khó hơn nhiều mà không có lý do chính đáng.


0

Bạn có thể sinh ra các quy trình và luồng để cho phép sử dụng hệ thống đa bộ xử lý đa lõi cho một chương trình trong trường hợp bạn không nhận được lợi ích (ít nhất là cho chương trình đơn lẻ) từ việc có nhiều luồng \ xử lý lõi.

Hoặc bạn có thể có các thói quen thăm dò ý kiến ​​cho một sự kiện thường chặn thực hiện thêm. Thay vào đó, buộc CPU bằng cách bỏ phiếu, thay vào đó, bạn có thể tạo một luồng sẽ ở trạng thái không hoạt động cho đến khi sự kiện thích hợp đánh thức nó. Phương pháp này được sử dụng rất phổ biến trong các máy chủ web và hàng đợi sự kiện GUI. Hầu hết các chương trình muốn có một số loại lưu trữ dữ liệu trung tâm (ngay cả khi mã thực thi chương trình của nó) mà tất cả các luồng có thể truy cập, vì vậy tôi đoán đó là lý do tại sao chúng sử dụng luồng trên các quy trình.


0

Các ứng dụng bạn đề cập hiếm khi chạy tất cả hàng chục chủ đề đó cùng một lúc. Hầu hết họ chỉ ngồi đó vì họ đang ở trong một nhóm . Ứng dụng này gửi các tác vụ khác nhau đến một hàng đợi, được thanh lọc bởi các luồng trong nhóm luồng.

Tại sao hồ bơi có kích thước lớn như vậy sau đó? Bởi vì, thường các luồng phải chờ các tài nguyên khác như đĩa, mạng, người dùng, một số luồng khác, v.v. Trong khi một luồng đang chờ, nên chạy các luồng khác để sử dụng đầy đủ bộ xử lý. Mặc dù kích thước hồ bơi thích hợp là khó khăn. Quá ít chủ đề và bạn sẽ mất hiệu suất vì bộ xử lý không được sử dụng đầy đủ trong khi chờ đợi một cái gì đó. Quá nhiều chủ đề và bạn sẽ mất hiệu suất do chuyển đổi giữa chúng.

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.