2008-09-19 39 views
13

Hãy tưởng tượng tôi có một chức năng trải qua một triệu/tỷ chuỗi và kiểm tra thông tin trong đó.Tăng tốc vòng lặp bằng cách sử dụng đa luồng trong C# (Câu hỏi)

f.ex:

foreach (String item in ListOfStrings) 
{ 
    result.add(CalculateSmth(item)); 
} 

nó tiêu thụ rất nhiều của thời gian, vì CalculateSmth là rất tốn thời gian chức năng.

Tôi muốn hỏi: cách tích hợp đa luồng trong quy trình loại này?

f.ex: Tôi muốn kích hoạt 5 chủ đề và mỗi chủ đề trả về một số kết quả, và các điều đó được tiếp tục cho đến khi danh sách có các mục.

Có lẽ bất cứ ai có thể hiển thị một số ví dụ hoặc các sản phẩm ..

Quên đề cập đến tôi cần nó trong .NET 2.0

+0

Bạn có cần kết quả theo thứ tự không? – Keith

+0

Bạn có thể sử dụng nhiều nhân viên nền không? tạo một số loại logic sẽ lấy tổng số danh sách các chuỗi sau đó tạo số lượng X của BW và chia mỗi số – Crash893

Trả lời

17

Bạn có thể thử các Parallel extensions (một phần của .NET 4.0)

Điều này cho phép bạn phải viết một cái gì đó như:

Parallel.Foreach (ListOfStrings, (item) => 
    result.add(CalculateSmth(item)); 
); 

Tất nhiên kết quả.bạn sẽ cần phải là chủ đề an toàn.

+0

trong trường hợp này, sẽ có bất kỳ điều kiện chủng tộc nào trong bộ sưu tập kết quả không? sau khi tất cả các chủ đề nhiều có thể được thực hiện result.add đồng thời ... – cruizer

+0

result.add phải được thread an toàn yeah .. – Tobi

+0

Quên đề cập đến tôi cần nó trong NET 2.0 –

1

Hiện tại tôi không có bất kỳ bài viết hay nào ở đây, nhưng những gì bạn muốn làm là điều gì đó cùng với Nhà sản xuất-Người tiêu dùng với Threadpool.

Các nhà sản xuất lặp lại và tạo các tác vụ (trong trường hợp này có thể chỉ cần xếp hàng các mục trong Danh sách hoặc Ngăn xếp). Người tiêu dùng, ví dụ, năm chủ đề mà đọc một mục ra khỏi ngăn xếp, tiêu thụ nó bằng cách tính toán nó, và sau đó lưu trữ nó ở nơi khác.

Bằng cách này, việc đa luồng được giới hạn chỉ trong năm luồng đó và tất cả chúng sẽ có tác dụng để làm cho đến khi ngăn xếp trống.

Những điều cần suy nghĩ về:

  • bảo vệ đặt trên đầu vào và đầu ra danh sách, chẳng hạn như một mutex.
  • Nếu thứ tự quan trọng, hãy đảm bảo rằng thứ tự đầu ra được duy trì. Một ví dụ có thể là lưu trữ chúng trong một SortedList hoặc một cái gì đó như thế.
  • Đảm bảo rằng CalculateSmth là chủ đề an toàn, không sử dụng bất kỳ trạng thái toàn cục nào.
2

Câu hỏi đầu tiên mà bạn phải trả lời là liệu bạn nên sử dụng luồng

Nếu CalculateSmth chức năng của bạn() về cơ bản là CPU-bound, tức là nặng trong CPU-sử dụng và về cơ bản không có I/O-sử dụng, sau đó tôi có một thời gian khó nhìn thấy điểm của việc sử dụng các chủ đề, vì các chủ đề sẽ cạnh tranh trên cùng một tài nguyên, trong trường hợp này là CPU.

Nếu CalculateSmth() của bạn đang sử dụng cả CPU và I/O, thì đó có thể là điểm trong việc sử dụng luồng.

Tôi hoàn toàn đồng ý với nhận xét cho câu trả lời của tôi. Tôi đã đưa ra một giả định sai lầm rằng chúng tôi đang nói về một CPU đơn với một lõi, nhưng những ngày này chúng tôi có CPU đa lõi, xấu của tôi.

+1

Phụ thuộc nếu đó là hệ thống đa lõi. Ví dụ, nếu bạn có bốn lõi, sau đó sử dụng bốn luồng sẽ thấy tốc độ xử lý xấp xỉ gấp bốn lần trong quá trình xử lý (giả sử không có sự liên kết phụ thuộc giữa các luồng). –

18

Các phần mở rộng song song là mát mẻ, nhưng điều này cũng có thể được thực hiện chỉ bằng cách sử dụng threadpool như thế này:

using System.Collections.Generic; 
using System.Threading; 

namespace noocyte.Threading 
{ 
    class CalcState 
    { 
     public CalcState(ManualResetEvent reset, string input) { 
      Reset = reset; 
      Input = input; 
     } 
     public ManualResetEvent Reset { get; private set; } 
     public string Input { get; set; } 
    } 

    class CalculateMT 
    { 
     List<string> result = new List<string>(); 
     List<ManualResetEvent> events = new List<ManualResetEvent>(); 

     private void Calc() { 
      List<string> aList = new List<string>(); 
      aList.Add("test"); 

      foreach (var item in aList) 
      { 
       CalcState cs = new CalcState(new ManualResetEvent(false), item); 
       events.Add(cs.Reset); 
       ThreadPool.QueueUserWorkItem(new WaitCallback(Calculate), cs); 
      } 
      WaitHandle.WaitAll(events.ToArray()); 
     } 

     private void Calculate(object s) 
     { 
      CalcState cs = s as CalcState; 
      cs.Reset.Set(); 
      result.Add(cs.Input); 
     } 
    } 
} 
+1

Và làm thế nào để bạn biết khi nào nó kết thúc? mmm. – leppie

+0

Có thể có ManualResetEvent mà chức năng WaitCallback gọi và chủ đề chính WaitOne bật. –

+0

Đã thêm mã để cho biết cách bạn có thể sử dụng MRE để thực hiện điều đó. – noocyte

12

Lưu ý rằng đồng thời không kỳ diệu cung cấp cho bạn nguồn tài nguyên hơn. Bạn cần phải thiết lập những gì đang làm chậm CalculateSmth xuống.

Ví dụ, nếu đó là CPU-ràng buộc (và bạn đang ở trên một lõi đơn) thì cùng một số CPU ticks sẽ đi đến mã, cho dù bạn thực hiện chúng tuần tự hoặc song song. Plus, bạn sẽ nhận được một số chi phí từ quản lý các chủ đề. Đối số tương tự áp dụng cho các ràng buộc khác (ví dụ: I/O)

Bạn sẽ chỉ nhận được hiệu suất trong trường hợp này nếu CalculateSmth rời khỏi tài nguyên miễn phí trong quá trình thực thi, có thể được sử dụng bởi một phiên bản khác. Đó không phải là hiếm. Ví dụ, nếu nhiệm vụ liên quan đến IO theo sau bởi một số công cụ CPU, sau đó quá trình 1 có thể làm công cụ CPU trong khi quá trình 2 đang làm IO. Như chiếu điểm, một chuỗi các đơn vị tiêu dùng sản xuất có thể đạt được điều này, nếu bạn có cơ sở hạ tầng.

5

Bạn cần phải chia nhỏ công việc bạn muốn làm song song. Dưới đây là ví dụ về cách bạn có thể chia công việc thành hai:

List<string> work = (some list with lots of strings) 

// Split the work in two 
List<string> odd = new List<string>(); 
List<string> even = new List<string>(); 
for (int i = 0; i < work.Count; i++) 
{ 
    if (i % 2 == 0) 
    { 
     even.Add(work[i]); 
    } 
    else 
    { 
     odd.Add(work[i]); 
    } 
} 

// Set up to worker delegates 
List<Foo> oddResult = new List<Foo>(); 
Action oddWork = delegate { foreach (string item in odd) oddResult.Add(CalculateSmth(item)); }; 

List<Foo> evenResult = new List<Foo>(); 
Action evenWork = delegate { foreach (string item in even) evenResult.Add(CalculateSmth(item)); }; 

// Run two delegates asynchronously 
IAsyncResult evenHandle = evenWork.BeginInvoke(null, null); 
IAsyncResult oddHandle = oddWork.BeginInvoke(null, null); 

// Wait for both to finish 
evenWork.EndInvoke(evenHandle); 
oddWork.EndInvoke(oddHandle); 

// Merge the results from the two jobs 
List<Foo> allResults = new List<Foo>(); 
allResults.AddRange(oddResult); 
allResults.AddRange(evenResult); 

return allResults; 
Các vấn đề liên quan