2012-03-16 20 views
10

Tôi có một ứng dụng tạo một vài trăm kết nối TCP cùng một lúc và nhận được một luồng dữ liệu liên tục từ chúng.Thay thế Socket.ReceiveAsync bằng NetworkStream.ReadAsync (awaitable)

private void startReceive() 
    { 
     SocketAsyncEventArgs e = new SocketAsyncEventArgs(); 
     e.Completed += receiveCompleted; 
     e.SetBuffer(new byte[1024], 0, 1024); 
     if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); } 
    } 

    void receiveCompleted(object sender, SocketAsyncEventArgs e) 
    { 
     ProcessData(e); 

     if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); } 
    } 

nỗ lực của tôi dẫn đến một cái gì đó như thế này:

private async void StartReceive() 
    { 
     byte[] Buff = new byte[1024]; 
     int recv = 0; 
     while (Socket.Connected) 
     { 
      recv = await NetworkStream.ReadAsync(Buff, 0, 1024); 
      ProcessData(Buff,recv); 
     } 
    } 

Vấn đề tôi đã có được phương pháp gọi StartReceive() sẽ chặn, và không đến được đi kèm StartSend() method called after StartReceive() . Creating a new task for StartReceive() would just end up with 300-ish threads, and it seems to do so just by calling StartReceive() `anyways.

Điều gì sẽ là phương pháp đúng đắn về thực hiện mới asyncawait từ khóa trên mã hiện tại của tôi trong khi sử dụng một NetworkStream vì vậy nó được sử dụng hồ bơi thread Socket.SendAsync()Socket.ReceiveAsync() đang sử dụng để tránh phải có hàng trăm đề/nhiệm vụ?

Có lợi thế về hiệu suất nào khi sử dụng networkstream theo cách này qua cổng hoàn thành i/o với beginreceive không?

+0

Nó không thực sự rõ ràng vấn đề là gì, hoặc ngữ cảnh - bạn nói về chặn phương thức gọi ... nó chặn gì? Đó không phải là vấn đề bạn cần sửa chưa? Vui lòng làm rõ câu hỏi của bạn - cũng như cho biết liệu tất cả "công việc" thực có bất kỳ mối quan hệ luồng nào không (ví dụ: với chuỗi giao diện người dùng). –

+0

Vấn đề chính là, phiên bản sử dụng đang chờ NetworkStream.ReadAsync khiến ứng dụng tạo một chuỗi cho mỗi kết nối. Ở đâu khi sử dụng Socket.ReceiveAsync dường như di chuột dưới 30 luồng cho tất cả 300 kết nối. – Josh

Trả lời

25

Bạn đang thay đổi hai việc cùng một lúc ở đây: phong cách không đồng bộ (SocketAsyncEventArgs để Task/async) và mức độ trừu tượng (Socket-NetworkStream).

Vì bạn đã thoải mái với Socket, tôi khuyên bạn chỉ nên thay đổi kiểu không đồng bộ và tiếp tục sử dụng trực tiếp lớp Socket.

Async CTP không cung cấp Socket bất kỳ phương pháp tương thích async nào (điều này thật kỳ lạ; Tôi cho rằng chúng bị bỏ sót do nhầm lẫn và sẽ được thêm vào trong .NET 4.5).

Nó không phải là khó khăn để tạo của riêng phương pháp của bạn ReceiveAsyncTask mở rộng (và giấy gói tương tự cho các hoạt động khác) nếu bạn sử dụng tôi AsyncEx library:

public static Task<int> ReceiveAsyncTask(this Socket socket, 
    byte[] buffer, int offset, int size) 
{ 
    return AsyncFactory<int>.FromApm(socket.BeginReceive, socket.EndReceive, 
     buffer, offset, size, SocketFlags.None); 
} 

Khi bạn làm điều đó, bạn StartReceive có thể được viết như vậy:

private async Task StartReceive() 
{ 
    try 
    { 
    var buffer = new byte[1024]; 
    while (true) 
    { 
     var bytesReceived = await socket.ReceiveAsyncTask(buffer, 0, 1024) 
      .ConfigureAwait(false); 
     ProcessData(buffer, bytesReceived); 
    } 
    } 
    catch (Exception ex) 
    { 
    // Handle errors here 
    } 
} 

Bây giờ, để giải quyết nhiều điểm nhỏ:

  • await không sinh ra một chuỗi mới. Tôi đã viết lên an async/await intro on my blog, cũng như nhiều người khác. async/await cho phép đồng thời, nhưng điều đó không nhất thiết ngụ ý đa luồng.
  • Hàng trăm chủ đề có thể có vấn đề. Tuy nhiên, hàng trăm nhiệm vụ không phải là vấn đề; hồ bơi thread và BCL được thiết kế để xử lý nhiều, nhiều tác vụ.
  • async/await không phải là một hình thức xử lý không đồng bộ hoàn toàn mới; nó chỉ là cách dễ dàng hơn tới thể hiện không đồng bộ. Nó vẫn sử dụng IOCP bên dưới.async/await có hiệu suất thấp hơn một chút so với các phương pháp cấp thấp hơn; sự hấp dẫn của nó là sự dễ dàng trong việc viết và soạn các phương thức không đồng bộ.
  • Một hệ thống rất bận rộn có thể thấy áp suất GC tăng lên khi nó chuyển sang async/await. Stephen Toub trong nhóm song song wrote up some example socket-specific awaitables có thể giúp giải quyết vấn đề đó. (Tôi khuyên bạn nên sử dụng mẫu đơn giản trước tiên, và chỉ sử dụng phương pháp nâng cao hiệu suất nếu bạn thấy cần thiết; vẫn còn, nó là tốt để biết nó ra khỏi đó nếu bạn cuối cùng cần nó).
  • Các phương pháp không đồng bộ phải trả lại Task trừ khi bạn thực sự cần chúng để trả lại void. Task đang chờ, vì vậy phương pháp của bạn có thể tổng hợp (và dễ dàng kiểm tra hơn); void giống như "lửa và quên".
  • Bạn có thể gọi ConfigureAwait(false) để nói cho phần còn lại của phương thức async để thực thi trên chuỗi chủ đề chuỗi. Tôi sử dụng điều này trong ví dụ trên để ProcessData được thực hiện trong một chuỗi chủ đề của chuỗi, giống như khi sử dụng SocketAsyncEventArgs.
  • Socket.Connected là vô ích. You need to send data to detect if the connection is still valid.
+0

Cảm ơn rất nhiều thông tin hữu ích này. Và tôi biết, tôi chỉ sử dụng Socket.Connected để viết nhanh. – Josh

+0

Điều này đã được triển khai trong .Net 4.5? – Isaac

+1

@Isaac: Không. Tôi nghĩ nhóm BCL đã quyết định sẽ dẫn đến quá nhiều API cho loại 'Socket'. –