2016-02-28 12 views
9

Tôi đã đọc các tài liệu của thư viện công việc và tài liệu TPL che phủ. Nhưng, tôi vẫn không thể hiểu rõ trường hợp sau đây rất rõ ràng và ngay bây giờ tôi cần phải thực hiện nó.Thực thi song song cho các hoạt động liên kết IO

Tôi sẽ đơn giản hóa tình huống của mình. Tôi có một số IEnumerable<Uri> có độ dài 1000. Tôi phải yêu cầu họ sử dụng HttpClient.

Tôi có hai câu hỏi.

  1. không tính toán nhiều, chỉ cần chờ yêu cầu Http. Trong trường hợp này tôi vẫn có thể sử dụng Parallel.Foreach()?
  2. Trong trường hợp sử dụng Task thay vào đó, phương pháp hay nhất để tạo số lượng lớn chúng là gì? Giả sử tôi sử dụng Task.Factory.StartNew() và thêm các tác vụ đó vào danh sách và chờ cho tất cả chúng. Có một tính năng (chẳng hạn như phân vùng TPL) kiểm soát số lượng tác vụ tối đa và tối đa HttpClient Tôi có thể tạo?

Có một số câu hỏi tương tự về SO, nhưng không ai đề cập đến số lượng tối đa . Yêu cầu chỉ là sử dụng các tác vụ tối đa với tối đa HttpClient.

Cảm ơn bạn trước.

Trả lời

11

Trong trường hợp này tôi vẫn có thể sử dụng Parallel.Foreach?

Điều này không thực sự phù hợp. Parallel.Foreach là công cụ chuyên sâu hơn cho CPU. Nó cũng không hỗ trợ các hoạt động không đồng bộ.

Trong trường hợp sử dụng Task thay vào đó, cách tốt nhất để tạo số lượng lớn chúng là gì?

Sử dụng khối TPL Dataflow thay thế. Bạn không tạo ra một số lượng lớn các nhiệm vụ mà ngồi đó chờ đợi cho một sợi để trở thành có sẵn. Bạn có thể cấu hình số tiền tối đa của nhiệm vụ và tái sử dụng chúng cho tất cả các mục trong khi đó ngồi trong một bộ đệm chờ đợi một nhiệm vụ. Ví dụ:

var block = new ActionBlock<Uri>(
    uri => SendRequestAsync(uri), 
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 }); 

foreach (var uri in uris) 
{ 
    block.Post(uri); 
} 

block.Complete(); 
await block.Completion; 
+0

Điều gì sẽ xảy ra nếu số lượng yêu cầu đồng thời vượt quá số lượng tối đa hệ điều hành yêu cầu http có thể thực hiện? – ozgur

+0

@ozgur Điều đó phụ thuộc vào nơi giới hạn được định cấu hình. Nhưng nếu có một thì hãy chắc chắn đặt 'MaxDegreeOfParallelism' thành cái gì đó thấp hơn thế. – i3arnon

+0

Câu hỏi cuối cùng. Ví dụ bạn cung cấp là thích hợp cho hoạt động IO nhưng nó không đòi hỏi song song cpu? – ozgur

12

Câu trả lời của i3arnon với TPL Dataflow là tốt; Dataflow rất hữu ích đặc biệt nếu bạn có sự kết hợp giữa mã CPU và I/O. Tôi sẽ lặp lại tình cảm của mình rằng Parallel được thiết kế cho mã ràng buộc CPU; nó không phải là giải pháp tốt nhất cho mã dựa trên I/O và đặc biệt là không thích hợp cho mã không đồng bộ.

Nếu bạn muốn một giải pháp thay thế mà hoạt động tốt với hầu hết-I O mã/- và không cần một thư viện bên ngoài - phương pháp bạn đang tìm kiếm là Task.WhenAll:

var tasks = uris.Select(uri => SendRequestAsync(uri)).ToArray(); 
await Task.WhenAll(tasks); 

Đây là giải pháp dễ nhất, nhưng nó có nhược điểm bắt đầu tất cả các yêu cầu cùng một lúc. Đặc biệt nếu tất cả các yêu cầu đều đi đến cùng một dịch vụ (hoặc một tập hợp các dịch vụ nhỏ), điều này có thể gây ra thời gian chờ. Để giải quyết điều này, bạn cần phải sử dụng một số loại điều chỉnh ...

Có tính năng nào (như phân vùng TPL) kiểm soát số lượng tác vụ tối đa và tối đa HttpClient tôi có thể tạo không?

TPL Dataflow có số đẹp MaxDegreeOfParallelism chỉ khởi động nhiều lần tại một thời điểm. Bạn cũng có thể tăng tốc mã không đồng bộ thường xuyên bằng cách sử dụng khác được xây dựng trong, SemaphoreSlim:

private readonly SemaphoreSlim _sem = new SemaphoreSlim(50); 
private async Task SendRequestAsync(Uri uri) 
{ 
    await _sem.WaitAsync(); 
    try 
    { 
    ... 
    } 
    finally 
    { 
    _sem.Release(); 
    } 
} 

Trong trường hợp sử dụng Task thay vào đó, các thực hành tốt nhất cho việc tạo ra số lượng lớn trong số họ là gì? Giả sử tôi sử dụng Task.Factory.StartNew() và thêm các tác vụ đó vào danh sách và chờ tất cả chúng.

Bạn thực sự không muốn sử dụng StartNew. Nó chỉ có một trường hợp sử dụng thích hợp (chủ nghĩa song song dựa trên nhiệm vụ động), điều cực kỳ hiếm. Mã hiện đại nên sử dụng Task.Run nếu bạn cần đẩy công việc lên chuỗi nền. Nhưng bạn thậm chí không cần điều đó để bắt đầu, vì vậy không phải StartNew cũng không phải Task.Run là thích hợp ở đây.

Có một số câu hỏi tương tự về SO, nhưng không có câu hỏi nào đề cập đến mức tối đa. Yêu cầu chỉ là sử dụng các tác vụ tối đa với tối đa HttpClient.

Tối đa là nơi mã không đồng bộ thực sự trở nên phức tạp. Với mã CPU (song song), giải pháp là hiển nhiên: bạn sử dụng nhiều luồng khi bạn có lõi. (Vâng, ít nhất bạn có thể bắt đầu ở đó và điều chỉnh khi cần thiết). Với mã không đồng bộ, không rõ ràng về giải pháp. Tùy thuộc vào nhiều yếu tố - số lượng bộ nhớ bạn có, cách máy chủ từ xa phản hồi (tỷ lệ giới hạn, thời gian chờ, v.v.), v.v.

Không có giải pháp dễ dàng nào ở đây. Bạn chỉ cần kiểm tra cách ứng dụng cụ thể của bạn giao dịch với mức đồng thời cao, và sau đó tăng tốc tới một số số thấp hơn.


Tôi có một số slides for a talk mà cố gắng để giải thích khi công nghệ khác nhau phù hợp (xử lý song song, không đồng pha, TPL Dataflow, và Rx). Nếu bạn thích mô tả bằng văn bản với công thức nấu ăn, tôi nghĩ bạn có thể hưởng lợi từ số my book đối với đồng thời.

+2

Khi bạn nói không có giải pháp dễ dàng, nó đã kết thúc cơn đau của tôi. Tôi đã nghĩ rằng có lẽ có một cách để làm điều đó và đang tìm kiếm ngày và đêm. Bây giờ tôi có thể cố gắng thực hiện một cái gì đó cụ thể cho tình hình của riêng tôi. Cảm ơn nhiều. – ozgur

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