2011-09-06 25 views
34

Tôi đang cố gắng hiểu rõ hơn về tùy chọn Async và Parallel mà tôi có trong C#. Trong các đoạn dưới đây, tôi đã bao gồm 5 phương pháp tôi gặp nhất. Nhưng tôi không chắc chắn đó để lựa chọn - hoặc tốt hơn nữa, những gì tiêu chí để xem xét khi lựa chọn:C# Tùy chọn không đồng bộ để xử lý danh sách

Phương pháp 1: Nhiệm vụ

(xem http://msdn.microsoft.com/en-us/library/dd321439.aspx)

Calling StartNew là chức năng tương đương để tạo một Task bằng cách sử dụng một trong các hàm khởi tạo của nó và sau đó gọi Start để lên lịch cho nó để thực thi. Tuy nhiên, trừ khi tạo và lập lịch phải được tách ra, StartNew là phương pháp được khuyến nghị cho cả sự đơn giản và hiệu suất.

Khởi động của TaskFactory Phương thức mới nên là cơ chế ưu tiên để tạo và lập lịch nhiệm vụ tính toán, nhưng đối với các trường hợp cần phải tạo và lập lịch biểu, có thể sử dụng phương thức khởi tạo và phương pháp Bắt đầu của nhiệm vụ nhiệm vụ thực hiện sau này.

// using System.Threading.Tasks.Task.Factory 
void Do_1() 
{ 
    var _List = GetList(); 
    _List.ForEach(i => Task.Factory.StartNew(_ => { DoSomething(i); })); 
} 

Cách 2: QueueUserWorkItem

(thấy http://msdn.microsoft.com/en-us/library/system.threading.threadpool.getmaxthreads.aspx)

Bạn có thể xếp hàng như nhiều yêu cầu bơi thread như bộ nhớ hệ thống cho phép. Nếu có nhiều yêu cầu hơn luồng chủ đề của chuỗi, yêu cầu bổ sung vẫn được xếp hàng đợi cho đến khi chuỗi chủ đề chuỗi trở nên khả dụng.

Bạn có thể đặt dữ liệu theo phương thức đã xếp hàng trong trường thể hiện của lớp mà phương thức được xác định hoặc bạn có thể sử dụng quá tải QueueUserWorkItem (WaitCallback, Object) chấp nhận đối tượng chứa dữ liệu cần thiết.

// using System.Threading.ThreadPool 
void Do_2() 
{ 
    var _List = GetList(); 
    var _Action = new WaitCallback((o) => { DoSomething(o); }); 
    _List.ForEach(x => ThreadPool.QueueUserWorkItem(_Action)); 
} 

Phương pháp 3: Parallel.Foreach

(xem: http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx)

Lớp Parallel cung cấp dữ liệu thư viện dựa trên thay thế song song cho các hoạt động phổ biến như cho vòng lặp, cho mỗi vòng lặp và thực hiện một tập hợp các câu lệnh.

Đại biểu cơ thể được gọi một lần cho mỗi phần tử trong nguồn có thể đếm được. Nó được cung cấp với phần tử hiện tại làm tham số.

// using System.Threading.Tasks.Parallel 
void Do_3() 
{ 
    var _List = GetList(); 
    var _Action = new Action<object>((o) => { DoSomething(o); }); 
    Parallel.ForEach(_List, _Action); 
} 

Phương pháp 4: IAsync.BeginInvoke

(xem: http://msdn.microsoft.com/en-us/library/cc190824.aspx)

BeginInvoke là không đồng bộ; do đó, điều khiển trả về ngay lập tức đối tượng gọi sau khi nó được gọi.

// using IAsync.BeginInvoke() 
void Do_4() 
{ 
    var _List = GetList(); 
    var _Action = new Action<object>((o) => { DoSomething(o); }); 
    _List.ForEach(x => _Action.BeginInvoke(x, null, null)); 
} 

Phương pháp 5: BackgroundWorker

(xem: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx)

Để thiết lập cho một hoạt động nền, thêm một event handler cho sự kiện DoWork. Gọi hoạt động tốn thời gian của bạn trong trình xử lý sự kiện này. Để bắt đầu hoạt động, hãy gọi RunWorkerAsync. Để nhận thông báo cập nhật tiến trình, hãy xử lý sự kiện ProgressChanged. Để nhận được thông báo khi thao tác hoàn tất, hãy xử lý sự kiện RunWorkerCompleted.

// using System.ComponentModel.BackgroundWorker 
void Do_5() 
{ 
    var _List = GetList(); 
    using (BackgroundWorker _Worker = new BackgroundWorker()) 
    { 
     _Worker.DoWork += (s, arg) => 
     { 
      arg.Result = arg.Argument; 
      DoSomething(arg.Argument); 
     }; 
     _Worker.RunWorkerCompleted += (s, arg) => 
     { 
      _List.Remove(arg.Result); 
      if (_List.Any()) 
       _Worker.RunWorkerAsync(_List[0]); 
     }; 
     if (_List.Any()) 
      _Worker.RunWorkerAsync(_List[0]); 
    } 
} 

Tôi cho rằng được những tiêu chí rõ ràng sẽ là:

  1. Là bất kỳ tốt hơn so với khác để thực hiện?
  2. Có tốt hơn cách khác để xử lý lỗi không?
  3. Có tốt hơn điểm khác để theo dõi/phản hồi không?

Nhưng, cách bạn chọn? Cảm ơn trước vì thông tin chi tiết của bạn.

+1

Bạn cũng có thể xem System.Reactive (phần mở rộng phản hồi hoặc rx.net). – lbergnehr

+0

Và bạn thậm chí không chạm vào CTP Async! :-) – xanatos

+0

Đó là sự thật, nhưng tôi đã gắn bó với C# 4. Điểm tốt, mặc dù. –

Trả lời

4

đầu tiên, thứ ba và ra ví dụ của bạn sử dụng ThreadPool ngầm bởi vì nhiệm vụ mặc định được sắp xếp trên ThreadPool và các phần mở rộng TPL sử dụng ThreadPool là tốt, API đơn giản là ẩn số của sự phức tạp thấy herehere. BackgroundWorkers là một phần của không gian tên ComponentModel vì chúng có nghĩa là để sử dụng trong các tình huống UI.

-2

Điều cuối cùng là tốt nhất cho 2,3 ít nhất. Nó có sẵn các phương thức/thuộc tính cho việc này. Các biến thể khác gần như giống nhau, chỉ các phiên bản khác nhau/trình bao bọc convinient

+0

Để bảo vệ bạn, chúng tôi đã sử dụng BackgroundWorker để thực hiện các thao tác không đồng bộ đơn giản trong phần lớn mã của chúng tôi. Nó đã làm việc cho chúng tôi cho nhiều phiên bản của khuôn khổ. Tuy nhiên, cách tiếp cận trong Do_5() không bao giờ chạy song song, chỉ là Async. Những cách tiếp cận khác có RẤT thiết bị tương tự, bằng cách này. Lý do tôi chọn BackgroundWorker ngày hôm nay sẽ là sự quen thuộc. Nó không giúp mã của tôi hoạt động tốt hơn vì giàn giáo của nó có vẻ nặng nề. –

2

Tiện ích mở rộng phản ứng là một thư viện sắp tới khác để xử lý lập trình không đồng bộ, đặc biệt khi nói đến thành phần của sự kiện và phương pháp không đồng bộ.

Nó không phải là bản địa, tuy nhiên nó được phát triển bởi phòng thí nghiệm Ms. Nó có sẵn cho cả .NET 3.5 và .NET 4.0 và về cơ bản là một tập hợp các phương thức mở rộng trên .NET 4.0 giới thiệu giao diện IObservable<T>.

Có rất nhiều ví dụ và hướng dẫn trên trang web chính của họ và tôi khuyên bạn nên kiểm tra một số trong số đó. Các mô hình có thể có vẻ hơi kỳ lạ lúc đầu (ít nhất là cho các lập trình viên .NET), nhưng cũng có giá trị nó, ngay cả khi nó chỉ nắm bắt khái niệm mới.

Sức mạnh thực sự của các phần mở rộng phản hồi (Rx.NET) là khi bạn cần soạn nhiều nguồn và sự kiện không đồng bộ. Tất cả các nhà khai thác được thiết kế với điều này trong tâm trí và xử lý các phần xấu xí của không đồng bộ cho bạn.

trang web chính: http://msdn.microsoft.com/en-us/data/gg577609

dẫn mới bắt đầu: http://msdn.microsoft.com/en-us/data/gg577611

Ví dụ:. http://rxwiki.wikidot.com/101samples

Điều đó nói rằng, mô hình async tốt nhất có thể phụ thuộc vào những gì tình huống mà bạn đang ở trong Một số là tốt hơn (đơn giản) cho các công cụ đơn giản hơn và một số có thể mở rộng và dễ xử lý hơn khi nói đến các tình huống phức tạp hơn. Tôi không thể nói cho tất cả những người bạn đang đề cập đến mặc dù.

+4

Câu trả lời này rất cần một số ngữ cảnh. Nó không phải cho đến khi đoạn thứ ba mà bạn đề cập đến những gì "nó" là bạn đang nói về. –

+0

Đã sửa lỗi. Xin lỗi về điều đó, bắt đầu như là một câu trả lời cho một bình luận và đã ra khỏi bối cảnh. – lbergnehr

15

Đi lấy những trong một trật tự tùy ý:

BackgroundWorker (#5)
Tôi thích sử dụng BackgroundWorker khi tôi đang làm việc với một giao diện người dùng. Lợi thế mà nó có là có sự kiện tiến bộ và hoàn thành cháy trên thread UI có nghĩa là bạn không có ngoại lệ khó chịu khi bạn cố gắng thay đổi các yếu tố giao diện người dùng. Nó cũng có một cách tốt đẹp được xây dựng trong cách báo cáo tiến độ. Một bất lợi mà chế độ này có là nếu bạn có các cuộc gọi chặn (như yêu cầu web) trong công việc của bạn, bạn sẽ có một thread ngồi xung quanh không làm gì trong khi công việc đang xảy ra. Điều này có thể không phải là một vấn đề nếu bạn chỉ nghĩ rằng bạn sẽ có một số ít trong số họ mặc dù.

IAsyncResult/Begin/End (APM, #4)
Đây là một mô hình phổ biến và mạnh mẽ nhưng rất khó để sử dụng. Lỗi xử lý là phiền hà vì bạn cần phải bắt lại ngoại lệ trên cuộc gọi kết thúc, và ngoại lệ chưa bắt buộc sẽ không nhất thiết phải làm cho nó trở lại bất kỳ phần có liên quan của mã có thể xử lý nó. Điều này có nguy cơ treo vĩnh viễn yêu cầu trong ASP.NET hoặc chỉ có lỗi bí ẩn biến mất trong các ứng dụng khác. Bạn cũng phải cảnh giác về tài sản CompletedSynchronously. Nếu bạn không theo dõi và báo cáo điều này đúng cách, chương trình có thể treo và rò rỉ tài nguyên. Mặt trái của điều này là nếu bạn đang chạy bên trong ngữ cảnh của một APM khác, bạn phải đảm bảo rằng mọi phương thức không đồng bộ mà bạn gọi cũng báo cáo giá trị này. Điều đó có nghĩa là thực hiện một cuộc gọi APM khác hoặc sử dụng Task và truyền nó đến số IAsyncResult để truy cập thuộc tính CompletedSynchronously của nó.

Ngoài ra còn có rất nhiều phí trong chữ ký: Bạn phải hỗ trợ một đối tượng tùy ý để thực hiện, thực hiện triển khai IAsyncResult của riêng bạn nếu bạn đang viết phương thức async hỗ trợ bỏ phiếu và chờ xử lý (ngay cả khi bạn chỉ sử dụng gọi lại). Nhân tiện, bạn chỉ nên sử dụng gọi lại ở đây. Khi bạn sử dụng thanh công cụ chờ hoặc phiếu thăm dò ý kiến ​​IsCompleted, bạn đang lãng phí một chuỗi trong khi thao tác đang chờ xử lý.

Event-based Asynchronous Pattern (EAP)
Một đó không phải trên danh sách của bạn nhưng tôi sẽ đề cập đến vì lợi ích của sự hoàn chỉnh. Đó là một chút thân thiện hơn APM. Có những sự kiện thay vì callbacks và có ít rác treo lên chữ ký phương thức. Xử lý lỗi dễ dàng hơn một chút vì nó được lưu và có sẵn trong gọi lại chứ không phải được ném lại. CompletedSynchronously cũng không phải là một phần của API.

Tasks (#1)
Nhiệm vụ là một API không đồng bộ thân thiện khác. Xử lý lỗi rất đơn giản: ngoại lệ luôn ở đó để kiểm tra cuộc gọi lại và không ai quan tâm đến CompletedSynchronously. Bạn có thể làm phụ thuộc và đó là một cách tuyệt vời để xử lý thực hiện nhiều tác vụ không đồng bộ. Bạn thậm chí có thể bọc APM hoặc EAP (một loại bạn bỏ qua) các phương thức không đồng bộ trong chúng. Một điều tốt khác về việc sử dụng các nhiệm vụ là mã của bạn không quan tâm cách hoạt động được thực hiện. Nó có thể chặn trên một sợi hoặc hoàn toàn không đồng bộ nhưng mã tiêu thụ không quan tâm đến điều này. Bạn cũng có thể dễ dàng kết hợp các hoạt động APM và EAP với Task.

Phương thức song song.For (# 3)
Đây là những người trợ giúp bổ sung ở trên cùng của Công việc. Họ có thể thực hiện một số công việc để tạo các nhiệm vụ cho bạn và làm cho mã của bạn dễ đọc hơn, nếu các tác vụ không đồng bộ của bạn phù hợp để chạy trong vòng lặp.

ThreadPool.QueueUserWorkItem (# 2)
Đây là một tiện ích ở mức độ thấp đó là thực sự được sử dụng bởi ASP.NET cho tất cả các yêu cầu. Nó không có bất kỳ lỗi xử lý được xây dựng trong như nhiệm vụ, do đó bạn phải nắm bắt tất cả mọi thứ và đường ống nó trở lại ứng dụng của bạn nếu bạn muốn biết về nó. Nó phù hợp cho công việc chuyên sâu của CPU nhưng bạn không muốn đặt bất kỳ cuộc gọi chặn nào vào đó, chẳng hạn như yêu cầu web đồng bộ. Đó là bởi vì miễn là nó chạy, nó sử dụng lên một sợi.

async/await Keywords
mới trong .NET 4.5, các từ khóa này cho phép bạn viết mã async mà không callbacks rõ ràng. Bạn có thể chờ đợi trên Task và bất kỳ mã nào bên dưới nó sẽ đợi cho phép hoạt động không đồng bộ đó hoàn thành mà không cần tốn một chuỗi.

+0

Tôi nghĩ rằng BackgroundWorker (# 5) bao gồm EAP. Nhưng, chắc chắn, cảm ơn vì những hiểu biết của bạn về câu hỏi. –

+0

BackgroundWorker thực hiện EAP, nhưng không "che" hoàn toàn. Với một BackgroundWorker bạn phải có một phương thức DoWork đồng bộ nhưng bạn có thể thực hiện một cuộc gọi EAP, nơi bạn xử lý các thứ khác nhau. Nó có thể bắt đầu một hoạt động không đồng bộ khác như một cuộc gọi web sau đó trở lại trên một chuỗi khác và kích hoạt sự kiện. Một số trường hợp nhất định có thể hấp dẫn hơn một Người làm nền tảng. – RandomEngy

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