2012-05-24 30 views
6

Có bất kỳ thay đổi nào mà nhiều Nhân viên nền thực hiện tốt hơn Task trên 5 quy trình đang chạy thứ hai không? Tôi nhớ đọc trong một cuốn sách mà một nhiệm vụ được thiết kế cho các quy trình chạy ngắn.Nhân viên nền công việc C#

Yêu cầu tôi hỏi là:

Tôi có quá trình mất 5 giây để hoàn thành và có 4000 quy trình để hoàn tất. Lúc đầu, tôi đã làm:

for (int i=0; i<4000; i++) { 
    Task.Factory.StartNewTask(action); 
} 

và điều này có hiệu suất kém (sau phút đầu tiên, 3-4 công việc hoàn thành và ứng dụng bàn điều khiển có 35 chủ đề). Có lẽ điều này là ngu ngốc, nhưng tôi nghĩ rằng hồ bơi thread sẽ xử lý loại tình huống này (nó sẽ đặt tất cả các hành động trong một hàng đợi, và khi một thread là miễn phí, nó sẽ có một hành động và thực hiện nó).

Bước thứ hai bây giờ là thực hiện thủ công Environment.ProcessorCác nhân viên nền tảng toàn cầu và tất cả các hành động được đặt trong ConcurentQueue. Vì vậy, mã sẽ trông giống như sau:

var workers = new List<BackgroundWorker>(); 
//initialize workers 

workers.ForEach((bk) => 
{ 
    bk.DoWork += (s, e) => 
    { 
     while (toDoActions.Count > 0) 
     { 
      Action a; 
      if (toDoActions.TryDequeue(out a)) 
      { 
       a(); 
      } 
     } 
    } 

    bk.RunWorkerAsync(); 
}); 

Cách này hoạt động tốt hơn. Nó thực hiện tốt hơn nhiều sau đó các nhiệm vụ ngay cả khi tôi đã có 30 công nhân nền (như nhiều nhiệm vụ như trong trường hợp đầu tiên).

LE:

tôi bắt đầu nhiệm vụ như thế này:

public static Task IndexFile(string file) 
    { 
     Action<object> indexAction = new Action<object>((f) => 
     { 
      Index((string)f); 
     }); 

     return Task.Factory.StartNew(indexAction, file); 
    } 

Và phương pháp Index là này một:

private static void Index(string file) 
    { 
     AudioDetectionServiceReference.AudioDetectionServiceClient client = new AudioDetectionServiceReference.AudioDetectionServiceClient(); 

     client.IndexCompleted += (s, e) => 
      { 
       if (e.Error != null) 
       { 
        if (FileError != null) 
        { 
         FileError(client, 
          new FileIndexErrorEventArgs((string)e.UserState, e.Error)); 
        } 
       } 
       else 
       { 
        if (FileIndexed != null) 
        { 
         FileIndexed(client, new FileIndexedEventArgs((string)e.UserState)); 
        } 
       } 
      }; 

     using (IAudio proxy = new BassProxy()) 
     { 
      List<int> max = new List<int>(); 
      if (proxy.ReadFFTData(file, out max)) 
      { 
       while (max.Count > 0 && max.First() == 0) 
       { 
        max.RemoveAt(0); 
       } 
       while (max.Count > 0 && max.Last() == 0) 
       { 
        max.RemoveAt(max.Count - 1); 
       } 

       client.IndexAsync(max.ToArray(), file, file); 
      } 
      else 
      { 
       throw new CouldNotIndexException(file, "The audio proxy did not return any data for this file."); 
      } 
     } 
    } 

phương pháp này đọc từ một mp3 nộp một số dữ liệu, sử dụng thư viện Bass.net. Sau đó, dữ liệu đó được gửi đến một dịch vụ WCF, sử dụng phương thức async. Phương thức IndexFile (chuỗi tệp), tạo nhiệm vụ được gọi cho 4000 lần trong vòng lặp for. Hai sự kiện đó, FileIndexed và FileError không được xử lý, vì vậy chúng không bao giờ bị ném.

+0

Bạn có thể muốn sử dụng 'BlockingCollection' thay vì' ConcurrentQueue' (nó sẽ sử dụng 'ConcurrentQueue' nội bộ). Nó sẽ làm cho mã một chút sạch hơn và dễ sử dụng hơn. – Servy

+0

Cảm ơn lời khuyên ... Tôi sẽ thay đổi :) –

+0

Bạn đã thử 'Parallel.Invoke' với một loạt các hành động chưa? – mellamokb

Trả lời

1

Lý do tại sao hiệu suất cho Công việc quá kém là do bạn đã gắn quá nhiều tác vụ nhỏ (4000). Hãy nhớ rằng CPU cần phải lên lịch các nhiệm vụ, vì vậy việc gắn một số nhiệm vụ ngắn ngủi sẽ làm cho tải công việc thêm cho CPU. Xem thêm thông tin có thể được tìm thấy trong đoạn thứ hai của TPL:

Bắt đầu với .NET Framework 4, TPL là cách ưa thích để ghi đa luồng và mã song song. Tuy nhiên, không phải tất cả mã là thích hợp để song song; ví dụ: nếu vòng lặp chỉ thực hiện lượng nhỏ công việc trên mỗi lần lặp lại hoặc không hoạt động cho nhiều lần lặp lại , thì chi phí song song có thể khiến mã số chạy chậm hơn.

Khi bạn sử dụng các công nhân nền, bạn giới hạn số lượng các đề sống tốt để các ProcessCount. Điều này làm giảm rất nhiều chi phí lên lịch.

0

Bạn đã cân nhắc sử dụng threadpool chưa?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

Nếu hiệu suất của bạn là chậm hơn khi sử dụng đề, nó chỉ có thể là do luồng trên không (phân bổ và phá hủy đề cá nhân).

+0

Vâng, các tác vụ được truy xuất từ ​​ThreadPool? Tôi đang tạo đối tượng Task, chứ không phải Thread. –

+0

Ông đã sử dụng TP, đó là khiếu nại. –

+1

Tác vụ sử dụng threadpool. –

1

Cho rằng bạn có danh sách các việc cần làm, tôi sẽ sử dụng lớp Parallel (hoặc For hoặc ForEach tùy thuộc vào điều gì phù hợp với bạn hơn). Ngoài ra bạn còn có thể vượt qua một tham số cấu hình cho bất kỳ của những phương pháp để kiểm soát có bao nhiêu công việc đang thực sự thực hiện cùng một lúc:

 System.Threading.Tasks.Parallel.For(0, 20000, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, i => 
     { 
      //do something 
     }); 

Đoạn mã trên sẽ thực hiện 20000 hoạt động, nhưng sẽ không thực hiện hơn 5 hoạt động tại cùng một lúc.

Tôi giải thích lý do nhân viên nền làm tốt hơn cho bạn là vì bạn đã tạo và khởi tạo ngay từ đầu, trong khi mã mẫu Task có vẻ như bạn đang tạo đối tượng Task mới cho mọi thao tác.Ngoài ra, bạn có nghĩ đến việc sử dụng số cố định Task đối tượng được khởi tạo ngay khi bắt đầu và sau đó thực hiện một hành động tương tự với một ConcurrentQueue giống như bạn đã làm với nhân viên nền không? Không. Điều đó cũng nên chứng minh là khá hiệu quả.

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