2012-06-01 22 views
5

Có lẽ tôi không hiểu nó đúng ... tất cả các vấn đề lớp song song: (Sử dụng System.Threading.Tasks.Parallel tạo chuỗi mới trong nhóm luồng?

Nhưng từ những gì tôi đọc bây giờ, tôi hiểu rằng khi tôi sử dụng song song tôi thực sự huy động tất cả các chủ đề tồn tại trong threadPool đối với một số nhiệm vụ/nhiệm vụ

Ví dụ:..

var arrayStrings = new string[1000]; 
    Parallel.ForEach<string>(arrayStrings, someString => 
    { 
     DoSomething(someString); 
    }); 

vì vậy, các Parallel.ForEach trong trường hợp này được huy động tất cả các chủ đề mà tồn tại trong ThreadPool cho 'DoSomething' nhiệm vụ/nhiệm vụ

Nhưng cuộc gọi Parallel.ForEach có tạo ra bất kỳ chuỗi mới nào không?

Rõ ràng là sẽ không có 1000 chủ đề mới. Nhưng cho phép giả định rằng có 1000 chủ đề mới, một số trường hợp rằng threadPool phát hành tất cả các chủ đề mà nó giữ như vậy, trong trường hợp này ... Parallel.ForEach sẽ tạo ra bất kỳ chủ đề mới?

+0

['Parallel.ForEach'] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx) -" Thực hiện một foreach (Đối với mỗi trong Visual Basic) hoạt động trong đó các lần lặp ** có thể ** chạy song song. " –

Trả lời

10

Câu trả lời ngắn: Parallel.ForEach() không “huy động tất cả chuỗi”. Và bất kỳ hoạt động nào lên lịch một số công việc trên ThreadPool (mà Parallel.ForEach() thực hiện) có thể gây ra việc tạo luồng mới trong hồ bơi.

Long trả lời: Để hiểu điều này đúng cách, bạn cần phải biết làm thế nào ba cấp độ công việc trừu tượng: Parallel.ForEach(), TaskSchedulerThreadPool:

  1. Parallel.ForEach() (và Parallel.For()) sắp xếp công việc của họ trên một TaskScheduler. Nếu bạn không chỉ định lịch trình rõ ràng, the current one sẽ được sử dụng.

    Parallel.ForEach() chia công việc giữa một số Task s. Mỗi Task sẽ xử lý một phần của chuỗi đầu vào và khi nó được thực hiện, nó sẽ yêu cầu một phần khác nếu có sẵn, v.v.

    Có bao nhiêu Task s sẽ Parallel.ForEach() tạo? Nhiều như TaskScheduler sẽ cho phép nó chạy. Cách này được thực hiện là mỗi Task đầu tiên enqueues một bản sao của chính nó khi nó bắt đầu thực hiện (trừ khi làm như vậy sẽ vi phạm MaxDegreeOfParallelism, nếu bạn đặt nó). Bằng cách này, mức độ tương tranh thực tế lên đến TaskScheduler.

    Ngoài ra, Task đầu tiên sẽ thực sự thực thi trên luồng hiện tại, nếu TaskScheduler hỗ trợ nó (điều này được thực hiện bằng cách sử dụng RunSynchronously()).

  2. The default TaskScheduler chỉ cần enqueues mỗi Task vào hàng đợi ThreadPool. (Trên thực tế, nó phức tạp hơn nếu bạn bắt đầu một Task từ một Task khác, nhưng điều đó không liên quan ở đây.) TaskScheduler s có thể làm những việc hoàn toàn khác nhau và một số trong số chúng (như TaskScheduler.FromCurrentSynchronizationContext()) hoàn toàn không phù hợp để sử dụng với Parallel.ForEach().

  3. ThreadPool sử dụng thuật toán khá phức tạp để quyết định chính xác có bao nhiêu luồng nên chạy tại bất kỳ thời điểm nào. Nhưng điều quan trọng nhất ở đây là lập kế hoạch mục công việc mới có thể gây ra việc tạo ra một chuỗi mới (mặc dù không nhất thiết phải ngay lập tức). Và bởi vì với Parallel.ForEach(), luôn luôn có một số mục xếp hàng để được thực hiện, nó hoàn toàn thuộc về thuật toán nội bộ của ThreadPool để quyết định số lượng chủ đề.

Kết hợp lại, không thể quyết định số lượng chủ đề sẽ được sử dụng bởi Parallel.ForEach(), bởi vì nó phụ thuộc vào nhiều biến. Cả hai thái cực đều có thể xảy ra: rằng vòng lặp sẽ chạy hoàn toàn đồng bộ trên luồng hiện tại và mỗi mục sẽ được chạy trên một chuỗi mới được tạo riêng của nó.

Nhưng nói chung, phải gần với hiệu quả tối ưu và có thể bạn không phải lo lắng về tất cả các chi tiết đó.

1

Parallel.Foreach không tạo chủ đề mới, cũng không "huy động tất cả chuỗi". Nó sử dụng một số lượng giới hạn các luồng từ threadpool và gửi các tác vụ cho chúng để thực thi song song. Trong thực hiện hiện tại, mặc định là sử dụng một luồng cho mỗi lõi.

+1

Điều đó không đúng. Nếu mã bên trong 'Parallel.ForEach()' chặn hoặc chạy trong một thời gian dài, nhiều luồng hơn số lõi sẽ được sử dụng. – svick

0

Song song không xử lý chủ đề gì cả - nó lên lịch cho NHIỆM VỤ cho khung công tác. Sau đó, có một lịch trình và lịch trình mặc định đi đến threadpool. Điều này sẽ cố gắng tìm một số goo của chủ đề (tốt hơn trong 4.5 hơn 4.0) và Threadpool có thể từ từ quay lên chủ đề mới.

Nhưng đó không phải là một functoin của parallel.foreach;)

các Parallel.ForEach sẽ tạo ra bất kỳ chủ đề mới ???

Nó sẽ không bao giờ xảy ra. Như tôi đã nói - nó có 1000 foreach, sau đó nó xếp hàng 10.000 nhiệm vụ, Point. Trình lập lịch tác vụ của nhà máy sẽ thực hiện những gì được lập trình để làm (bạn có thể thay thế nó) Nói chung, mặc định - vâng, từ từ chủ đề mới sẽ khởi động WITHIN REASON

+0

'Parallel.ForEach()' trên một tập hợp các mục * n * nói chung sẽ không tạo * n * 'Nhiệm vụ, điều đó có thể quá kém hiệu quả. Nó phân vùng bộ sưu tập nguồn và chỉ tạo ra nhiều 'Task' như 'TaskScheduler' cho phép nó chạy. – svick

1

Tôi nghĩ rằng bạn có cách này sai vòng. từ PATTERNS OF PARALLEL PROGRAMMING bạn sẽ thấy rằng Parallel.ForEach là đường chỉ thực sự cú pháp.

các Parallel.ForEach phần lớn là luộc xuống một cái gì đó như thế này,

for (int p = 0; p < arrayStrings.Count(); p++) 
{ 
    ThreadPool.QueueUserWorkItem(DoSomething(arrayStrings[p]); 
} 

các ThreadPool sẽ chăm sóc của lịch trình. có một số bài viết tuyệt vời về cách trình lên lịch của ThreadPool hoạt động ở một mức độ nào đó f bạn quan tâm, nhưng đó là không có gì để làm với TPL.

+1

'Thread mới()' sẽ luôn tạo một luồng mới, không sử dụng một luồng từ threadpool.Mã bạn đăng sẽ luôn tạo nhiều chuỗi vì có các mục trong bộ sưu tập. Điều này không có cách nào đại diện cho Parallel.ForEach. –

+0

@AllonGuralnek bạn chính xác, cập nhật. –

+1

'Parallel.ForEach()' không được xây dựng trên đầu trang 'ThreadPool', nó được xây dựng trên đầu trang của' TaskScheduler'. Ngoài ra, nó thông minh hơn về mã trong mỗi 'Task', do đó không có một' Task' cho mỗi mục. Một điều nữa là mã của bạn sẽ không chặn, nhưng 'Parallel.ForEach()' thực hiện. – svick

Các vấn đề liên quan