Chủ đề vs ThreadPool


137

Sự khác biệt giữa việc sử dụng một luồng mới và sử dụng một luồng từ nhóm luồng là gì? Có những lợi ích hiệu suất nào và tại sao tôi nên xem xét sử dụng một luồng từ nhóm chứ không phải là một thứ tôi đã tạo rõ ràng? Tôi đang nghĩ cụ thể về .NET ở đây, nhưng các ví dụ chung đều ổn.

Câu trả lời:


110

Nhóm luồng sẽ cung cấp lợi ích cho các hoạt động thường xuyên và tương đối ngắn bằng cách

  • Sử dụng lại các chủ đề đã được tạo thay vì tạo các chủ đề mới (một quy trình đắt tiền)
  • Điều chỉnh tốc độ tạo luồng khi có một loạt các yêu cầu cho các mục công việc mới (tôi tin rằng điều này chỉ có trong .NET 3.5)

    • Nếu bạn xếp hàng 100 tác vụ nhóm luồng, nó sẽ chỉ sử dụng nhiều luồng như đã được tạo để phục vụ các yêu cầu này (ví dụ 10 chẳng hạn). Nhóm luồng sẽ thực hiện kiểm tra thường xuyên (tôi tin rằng cứ sau 500ms trong 3.5 SP1) và nếu có các tác vụ xếp hàng, nó sẽ tạo một luồng mới. Nếu các tác vụ của bạn nhanh, thì số lượng chủ đề mới sẽ nhỏ và việc sử dụng lại 10 chủ đề cho các tác vụ ngắn sẽ nhanh hơn việc tạo ra 100 chủ đề trước.

    • Nếu khối lượng công việc của bạn liên tục có số lượng lớn yêu cầu nhóm luồng đến, thì nhóm luồng sẽ tự điều chỉnh khối lượng công việc của bạn bằng cách tạo thêm luồng trong nhóm theo quy trình trên để có số lượng luồng lớn hơn có sẵn để xử lý yêu cầu

    • kiểm tra ở đây để biết thêm thông tin chi tiết về cách thức hoạt động của nhóm luồng dưới mui xe

Tự tạo một chủ đề mới sẽ phù hợp hơn nếu công việc sẽ diễn ra tương đối dài (có thể khoảng một hoặc hai giây, nhưng nó phụ thuộc vào tình huống cụ thể)

@Krzysztof - Chủ đề Pool Pool là chủ đề nền sẽ dừng khi chủ đề chính kết thúc. Theo mặc định, các luồng được tạo thủ công được tạo nền trước (sẽ tiếp tục chạy sau khi luồng chính kết thúc), nhưng có thể được đặt thành nền trước khi gọi Bắt đầu trên chúng.


5
Điều duy nhất tôi băn khoăn là câu lệnh sau từ MSDN ( msdn.microsoft.com/en-us/l Library / 1c9txz50.aspx ) "Một luồng nền chỉ thực thi khi số lượng luồng xử lý nền trước nhỏ hơn số lượng bộ xử lý . ". Vì vậy, điều đó có nghĩa là khi phân chia công việc giữa các lõi mà các luồng tiền cảnh được ưu tiên?
cdiggins

1
Bạn không thể hủy bỏ hoặc làm gián đoạn một luồng từ nhóm luồng. Bạn không thể tham gia một chuỗi từ nhóm luồng. Để đạt được điều đó, bạn phải sử dụng một số cơ chế khác
Zinov

14

Chủ đề quản lý .NET: -

  • Kích thước chính nó dựa trên khối lượng công việc hiện tại và phần cứng có sẵn
  • Chứa các luồng công nhân và các luồng cổng hoàn thành (được sử dụng cụ thể để phục vụ IO)
  • Được tối ưu hóa cho một số lượng lớn các hoạt động tương đối ngắn

Các triển khai nhóm luồng khác tồn tại có thể phù hợp hơn cho các hoạt động dài hạn.

Cụ thể, sử dụng nhóm luồng để ngăn ứng dụng của bạn tạo quá nhiều luồng. Các tính năng quan trọng nhất của một threadpool là hàng đợi công việc. Đó là, một khi máy của bạn đủ bận rộn, luồng xử lý sẽ xếp hàng các yêu cầu thay vì ngay lập tức tạo ra nhiều luồng hơn.

Vì vậy, nếu bạn sẽ tạo một số lượng nhỏ các chủ đề giới hạn, hãy tự tạo chúng. Nếu bạn không thể xác định trước có bao nhiêu luồng có thể được tạo (ví dụ: chúng được tạo để đáp ứng với IO đến) và công việc của chúng sẽ tồn tại trong thời gian ngắn, hãy sử dụng luồng này. Nếu bạn không biết có bao nhiêu, nhưng công việc của họ sẽ hoạt động lâu dài, không có gì trong nền tảng để giúp bạn - nhưng bạn có thể tìm thấy các triển khai luồng thay thế phù hợp.


Trong .NET, bạn có thể sử dụng các cổng hoàn thành mà không có nhóm luồng không? Tôi đã giả định rằng các phương thức I / O không đồng bộ là cách duy nhất (trong .NET) và chúng sử dụng nhóm luồng
Karg

10

cũng thế

new Thread().Start()

sinh ra chủ đề Tiền cảnh sẽ không chết nếu bạn đóng chương trình của bạn. Chủ đề ThreadPool là chủ đề nền chết khi bạn đóng ứng dụng.


11
Bạn luôn có thể đặt một chủ đề để nền. Chúng chỉ là tiền cảnh theo mặc định.
Kris Erickson

3
nemo: var t = new Chủ đề (...); t.BackgroundThread = true; t.Start ();
Ricardo Amores

18
Làm rõ về thuật ngữ "chương trình". Một ứng dụng máy tính để bàn chạy trong một quy trình và có ít nhất một luồng tiền cảnh quản lý giao diện người dùng. Quá trình đó sẽ tiếp tục chạy miễn là nó có các luồng tiền cảnh. Khi bạn đóng một ứng dụng máy tính để bàn, luồng UI nền trước dừng lại, nhưng bạn không nhất thiết phải dừng quá trình nếu nó có các luồng nền trước khác.
G-Wiz

8

Tôi tò mò về việc sử dụng tài nguyên tương đối cho những thứ này và đã chạy một điểm chuẩn trên máy tính xách tay Intel i5 lõi kép 2012 của tôi bằng cách sử dụng bản phát hành .net 4.0 trên windows 8. Thread Pools mất trung bình 0,035ms để bắt đầu trong đó Chủ đề lấy trung bình 5,06 bệnh đa xơ cứng. Nói cách khác, Thread trong pool bắt đầu nhanh hơn khoảng 300 lần đối với số lượng lớn các luồng sống ngắn. Ít nhất là trong các phạm vi được kiểm tra (100-2000), tổng thời gian trên mỗi luồng dường như không đổi.

Đây là mã đã được điểm chuẩn:

    for (int i = 0; i < ThreadCount; i++) {
        Task.Run(() => { });
    }

    for (int i = 0; i < ThreadCount; i++) {
        var t = new Thread(() => { });
        t.Start();
    }

nhập mô tả hình ảnh ở đây


5
Tôi nghĩ đó là bởi vì ThreadPool sử dụng lại các chủ đề đã tạo thay vì tạo ra các chủ đề mới (rất tốn kém)
Fabriciorissetto


1

Chủ đề lưu trữ cục bộ không phải là một ý tưởng tốt với nhóm chủ đề. Nó cung cấp cho chủ đề một "bản sắc"; không phải tất cả các chủ đề là bằng nhau nữa. Bây giờ nhóm luồng đặc biệt hữu ích nếu bạn chỉ cần một loạt các luồng giống hệt nhau, sẵn sàng thực hiện công việc của bạn mà không cần tạo chi phí.


1

Nếu bạn cần nhiều chủ đề, có lẽ bạn muốn sử dụng ThreadPool. Họ sử dụng lại các chủ đề giúp bạn tiết kiệm chi phí cho việc tạo chủ đề.

Nếu bạn chỉ cần một chủ đề để hoàn thành công việc, thì chủ đề có lẽ là dễ nhất.


1

Nhu cầu chính đối với các chủ đề theadpool là xử lý các nhiệm vụ nhỏ ngắn dự kiến ​​sẽ hoàn thành gần như ngay lập tức. Trình xử lý ngắt phần cứng thường chạy trong ngữ cảnh xếp chồng không phù hợp với mã không phải kernel, nhưng trình xử lý ngắt phần cứng có thể phát hiện ra rằng gọi lại hoàn thành I / O ở chế độ người dùng nên được chạy càng sớm càng tốt. Tạo một chủ đề mới cho mục đích chạy một thứ như vậy sẽ là quá mức cần thiết. Có một vài luồng được tạo trước có thể được gửi để chạy các cuộc gọi lại hoàn thành I / O hoặc những thứ tương tự khác sẽ hiệu quả hơn nhiều.

Một khía cạnh quan trọng của các luồng như vậy là nếu các phương thức hoàn thành I / O luôn hoàn thành về cơ bản tức thời và không bao giờ chặn, và số lượng các luồng đó hiện đang chạy các phương thức đó ít nhất bằng với số lượng bộ xử lý, cách duy nhất bất kỳ luồng nào khác có thể chạy trước khi một trong các phương thức đã nói ở trên kết thúc nếu một trong các phương thức khác chặn hoặc thời gian thực hiện của nó vượt quá một lát cắt thời gian luồng thông thường; không ai trong số đó sẽ xảy ra rất thường xuyên nếu nhóm luồng được sử dụng như dự định.

Nếu một phương thức không thể được dự kiến ​​sẽ thoát trong vòng 100ms hoặc lâu hơn khi nó bắt đầu thực thi, thì phương thức sẽ được thực thi thông qua một số phương tiện khác ngoài nhóm luồng chính. Nếu một công việc có nhiều nhiệm vụ phải thực hiện chuyên sâu về CPU nhưng sẽ không chặn, thì có thể hữu ích khi gửi chúng bằng cách sử dụng một nhóm các luồng ứng dụng (một cho mỗi lõi CPU) tách biệt với luồng xử lý "chính", kể từ khi sử dụng nhiều luồng hơn lõi sẽ phản tác dụng khi chạy các tác vụ không cần CPU. Tuy nhiên, nếu một phương thức sẽ mất một giây hoặc lâu hơn để thực thi và sẽ dành phần lớn thời gian bị chặn, thì phương thức đó có thể sẽ được chạy trong một luồng chuyên dụng và gần như chắc chắn không nên chạy trong một luồng chính. Nếu một hoạt động chạy dài cần được kích hoạt bởi một cái gì đó như gọi lại I / O,


0

Nói chung (tôi chưa bao giờ sử dụng .NET), một nhóm luồng sẽ được sử dụng cho mục đích quản lý tài nguyên. Nó cho phép các ràng buộc được cấu hình vào phần mềm của bạn. Nó cũng có thể được thực hiện vì lý do hiệu suất, vì việc tạo chủ đề mới có thể tốn kém.

Cũng có thể có lý do cụ thể hệ thống. Trong Java (một lần nữa tôi không biết nếu điều này áp dụng cho .NET), trình quản lý các luồng có thể áp dụng các biến cụ thể của luồng khi mỗi luồng được kéo từ nhóm và bỏ đặt chúng khi chúng được trả về (cách phổ biến để truyền một cái gì đó như một bản sắc).

Ràng buộc ví dụ: Tôi chỉ có 10 kết nối db, vì vậy tôi sẽ chỉ cho phép 10 luồng công nhân để truy cập cơ sở dữ liệu.

Điều này không có nghĩa là bạn không nên tạo chủ đề của riêng mình, nhưng có những điều kiện theo đó có ý nghĩa khi sử dụng nhóm.


0

Sử dụng một nhóm là một ý tưởng tốt, nếu bạn không biết hoặc không thể kiểm soát số lượng luồng sẽ được tạo.

Chỉ cần có một vấn đề với một biểu mẫu sử dụng luồng để cập nhật một số trường từ cơ sở dữ liệu về một sự kiện thay đổi vị trí của điều khiển danh sách (tránh freez). Phải mất 5 phút để người dùng của tôi gặp lỗi từ cơ sở dữ liệu (quá nhiều liên kết với Access) vì anh ta đã thay đổi vị trí danh sách quá nhanh ...

Tôi biết có một cách khác để giải quyết vấn đề cơ bản (bao gồm cả việc không sử dụng quyền truy cập) nhưng gộp chung là một khởi đầu tốt.


0

Chủ đề :

  1. Tạo một Thread chậm hơn nhiều so với sử dụng Thread-pool.
  2. Bạn có thể thay đổi mức độ ưu tiên của một chủ đề.
  3. Số lượng chủ đề tối đa trong một quy trình liên quan đến tài nguyên.
  4. Thread ở cấp độ HĐH và được điều khiển bởi HĐH.
  5. Sử dụng Thread là một tùy chọn tốt hơn khi tác vụ tương đối dài

Bể bơi theo chủ đề :

  1. Chạy Thread trên pool-thread nhanh hơn nhiều so với trực tiếp tạo Thread.
  2. Bạn không thể thay đổi mức độ ưu tiên của một luồng chạy dựa trên Thread-pool.
  3. Chỉ có một nhóm Thread-pool cho mỗi quá trình.
  4. Nhóm luồng được quản lý bởi CLR.
  5. Thread-pool rất hữu ích cho hoạt động ngắn hạn.
  6. Số lượng Chủ đề trong Nhóm luồng có liên quan đến tải ứng dụng.
  7. Các tác vụ TPL chạy dựa trên Thread-pool
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.