2013-08-21 39 views
8

Tôi đã cố gắng đọc các phương thức không đồng bộ và hiện đang cố gắng tạo phương thức không đồng bộ của riêng mình. Phương thức là một cuộc gọi webservice trả về một danh sách các bản ghi lỗi. Tôi không chắc rằng tôi đã hiểu chính xác vì vậy tôi nghĩ rằng tôi muốn chia sẻ mã của tôi để xem nếu tôi nên làm bất cứ điều gì khác nhau.Tạo phương thức dịch vụ web không đồng bộ

Tất cả những gì tôi muốn làm là trả về danh sách các bản ghi lỗi bằng cách gọi phương thức GetAllErrorLogs(), đó là phương thức được đồng bộ hóa. Vì có thể mất một giây để lấy tất cả các bản ghi lỗi, tôi muốn có cơ hội thực hiện các công cụ khác khi tôi gọi phương thức GetAllErrorLogs(). Đây là mã.

[WebMethod] 
public async Task<List<ErrorLog>> GetAllErrorLogs() 
{ 
    List<ErrorLog> errorLogs = new List<ErrorLog>(); 

    await System.Threading.Tasks.Task.Run(() => { 
     errorLogs = ErrorLogRepository.GetAllErrorLogs(); 
    }); 


    if (errorLogs == null) 
     return new List<ErrorLog>(); 

    return errorLogs; 
} 

Cảm ơn!

+1

Tôi không thấy nhiều lợi thế sử dụng async/chờ đợi ở phía máy chủ. Bạn chỉ cần sử dụng nhiều chủ đề cho cùng một điều. – I4V

+4

@ I4V: 'async' ở phía máy chủ có thể giảm đáng kể * số lượng chủ đề được sử dụng cho mỗi yêu cầu (giả sử mã tự nhiên không đồng bộ và không giả mạo không đồng bộ như 'Task.Run'). Kết quả là, các máy chủ không đồng bộ có thể mở rộng tốt hơn, thường là theo thứ tự 10-100x. –

+0

Stephan Cleary là chính xác ... Tôi đã tham dự một khóa đào tạo tại một cơ sở microsoft, nơi chúng tôi đã nói rằng chỉ bằng cách đồng bộ hóa tất cả các mã chạy trên máy chủ của bạn, bạn sẽ nhận được tăng công suất lớn cho cùng một phần cứng. Hoàn toàn bởi vì, khi một phương thức đang chờ phản hồi từ một cuộc gọi phụ, luồng chính được giải phóng để đi và thực hiện công việc khác ... tức là đối phó với các yêu cầu web đồng thời khác. Ưu điểm là đa nhiệm. – GPR

Trả lời

7

Gần đây tôi đã nói chuyện tại ThatConference trên async on the server side và tôi giải quyết vấn đề này trong các trang trình bày.

Ở phía máy chủ, bạn muốn tránh sử dụng Task.Run và các cấu trúc khác xếp hàng hoạt động với nhóm luồng. Càng nhiều càng tốt, giữ các chủ đề của chuỗi chủ đề có sẵn để xử lý các yêu cầu.

Vì vậy, lý tưởng là kho lưu trữ của bạn sẽ có phương thức không đồng bộ GetAllErrorLogsAsync, chính nó sẽ không đồng bộ. Nếu GetAllErrorLogs không thể không đồng bộ, thì bạn cũng có thể gọi trực tiếp ngay (xóa await Task.Run).

Vì có thể mất một giây để tìm nạp tất cả nhật ký lỗi Tôi muốn có cơ hội thực hiện các công cụ khác khi tôi gọi phương thức GetAllErrorLogs().

Nếu bạn có sẵn GetAllErrorLogsAsync thì có thể dễ dàng thực hiện bằng cách sử dụng Task.WhenAll. Tuy nhiên, nếu GetAllErrorLogs đồng bộ, thì bạn chỉ có thể thực hiện việc này bằng cách thực hiện công việc song song trong yêu cầu của mình (ví dụ: nhiều cuộc gọi đến Task.Run theo sau là Task.WhenAll).

Mã song song trên máy chủ phải được tiếp cận với sự lo lắng lớn. Nó chỉ được chấp nhận trong một tập hợp rất hạn chế các kịch bản. Toàn bộ điểm của async ở phía máy chủ là sử dụng ít hơn chủ đề cho mỗi yêu cầu và khi bạn bắt đầu song song, bạn đang làm ngược lại: nhiều chủ đề cho mỗi yêu cầu. Điều này chỉ thích hợp nếu bạn biết cơ sở người dùng của mình rất nhỏ; nếu không, bạn sẽ giết khả năng mở rộng máy chủ của mình.

+0

Cảm ơn bạn đã trả lời! – Andreas

+0

Sự phức tạp của tính song song quá dài đối với ở đây và ngoài chủ đề, nhưng cần lưu ý rằng cảnh báo chống lại song song ở đây là quá tải. Nếu bạn đang chờ một yêu cầu web đến một dịch vụ web và một cuộc gọi cơ sở dữ liệu, bạn đang tận dụng các chủ đề cổng hoàn thành I/O siêu rẻ, OS-level và không ăn chủ đề từ ThreadPool. Một kịch bản hoàn hảo cho tính song song giúp tăng tốc độ cho người dùng mà không làm bùng nổ Request Threadpool. Câu trả lời đúng ở đây sẽ là "Đây là cách" chứ không phải "Đừng làm điều này." http://stackoverflow.com/a/539968/176877 –

+0

@ChrisMoschini: Tôi phân biệt giữa * đa luồng/song song * ('Task.Run',' Parallel', v.v.) và * asynchrony * ('async',' await', vv), cả hai đều là [các dạng khác nhau của * đồng thời *] (https://pbs.twimg.com/media/B63AADfIgAA4jPH.jpg:large). Với những định nghĩa đó, song song là xấu ở phía máy chủ. –

0

** Đây là khả năng sai, đọc ý kiến ​​hoặc câu hỏi spinoff tại HttpContext.Current after an await

Nếu ErrorLogRepository.GetAllErrorLogs() không phải là thread an toàn, nó sẽ gây ra lỗi lạ và có khả năng ngoại lệ. Đảm bảo mã của bạn đã sẵn sàng cho hoạt động đa luồng trước khi chuyển sang phương thức không đồng bộ, điều này rõ ràng là lời khuyên rất nhỏ nhưng thường bị bỏ qua. Ví dụ: nếu bạn tham chiếu HttpContext.Current trong các phương pháp của mình, mã của bạn sẽ chết theo phương thức không đồng bộ và đôi khi thậm chí SAU là await. Lý do là mã trong khối async sẽ có khả năng chạy trên một chuỗi riêng biệt, mà sẽ không có quyền truy cập vào cùng một thuộc tính luồng tĩnh HttpContext.Currentawait được biên dịch thành hai phương thức. Tất cả các mã trước khi một await được chạy trên một sợi, và sau đó gọi mã sau một từ khóa chờ đợi như là một sự tiếp nối, nhưng có khả năng trên một chủ đề khác. Vì vậy, đôi khi mã của bạn thậm chí sẽ hoạt động trong một khối không đồng bộ, chỉ để nghẹt thở bất ngờ sau khi nó được "ra" của async trở lại những gì bạn nghĩ là một phần đồng bộ của mã của bạn (nhưng trong thực tế tất cả mọi thứ sau khi một từ khóa await đã không được bảo đảm làm chủ đề ban đầu).

+0

Có rất nhiều thông tin sai lạc trong câu trả lời này. 'UnobservedTaskException' không được yêu cầu ở đây (' await' sẽ truyền bá đúng bất kỳ ngoại lệ nào từ 'GetAllErrorLogs' thông qua phương thức WebAPI). 'HttpContext.Current' được truyền cho tất cả các luồng xử lý yêu cầu async theo mặc định (tức là dòng' return errorLogs' có 'HttpContext.Current' hoàn toàn hợp lệ). –

+0

Bạn đang đúng về chờ đợi, tôi hiểu sai phần đó. Nó sẽ không chỉ được thúc đẩy trong trường hợp Task.Run đang được sử dụng như một ngọn lửa và quên. Tuy nhiên, HttpContext.Current sẽ chắc chắn không hợp lệ sau khi chờ đợi trừ khi WebAPI đã ghi đè hành vi ngữ cảnh đồng bộ hóa mặc định. – welegan

+1

WebAPI không cung cấp ngữ cảnh đồng bộ hóa, nhưng ASP.NET thực hiện. Vì vậy, 'HttpContext.Current' là hoàn toàn hợp lệ sau khi' chờ đợi'. –

0

Dưới đây là một số mã sản xuất ...

using System.Web.Http; 
using AysncTask = System.Threading.Tasks.Task; 

public class myController : ApiControllerBase 
{ 
     [HttpPut] 
     [Route("api/cleardata/{id}/{requestId}/")] 
     public async AysncTask ClearData(Guid id, Guid requestId) 
     { 
      try 
      { 
       await AysncTask.Run(() => DoClearData(id, requestId)); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception("Exception in myController.ClearData", ex); 
      } 
     } 
} 
0

Xử lý ngoại lệ Async cũng là rất rất quan trọng .. mặc dù đây là một ứng dụng cửa sổ giao diện điều khiển, cùng một nguyên tắc nên áp dụng.

nguồn: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/

using System; 
    using System.Runtime.CompilerServices; 
    using System.Threading; 
    using System.Threading.Tasks; 

    namespace AsyncAndExceptions 
    { 
class Program 
{ 
    static void Main(string[] args) 
    { 
    AppDomain.CurrentDomain.UnhandledException += (s, e) => Log("*** Crash! ***", "UnhandledException"); 
    TaskScheduler.UnobservedTaskException += (s, e) => Log("*** Crash! ***", "UnobservedTaskException"); 

    RunTests(); 

    // Let async tasks complete... 
    Thread.Sleep(500); 
    GC.Collect(3, GCCollectionMode.Forced, true); 
    } 

    private static async Task RunTests() 
    { 
    try 
    { 
     // crash 
     // _1_VoidNoWait(); 

     // crash 
     // _2_AsyncVoidAwait(); 

     // OK 
     // _3_AsyncVoidAwaitWithTry(); 

     // crash - no await 
     // _4_TaskNoWait(); 

     // crash - no await 
     // _5_TaskAwait(); 

     // OK 
     // await _4_TaskNoWait(); 

     // OK 
     // await _5_TaskAwait(); 
    } 
    catch (Exception ex) { Log("Exception handled OK"); } 

    // crash - no try 
    // await _4_TaskNoWait(); 

    // crash - no try 
    // await _5_TaskAwait(); 
    } 

    // Unsafe 
    static void _1_VoidNoWait() 
    { 
    ThrowAsync(); 
    } 

    // Unsafe 
    static async void _2_AsyncVoidAwait() 
    { 
    await ThrowAsync(); 
    } 

    // Safe 
    static async void _3_AsyncVoidAwaitWithTry() 
    { 
    try { await ThrowAsync(); } 
    catch (Exception ex) { Log("Exception handled OK"); } 
    } 

    // Safe only if caller uses await (or Result) inside a try 
    static Task _4_TaskNoWait() 
    { 
    return ThrowAsync(); 
    } 

    // Safe only if caller uses await (or Result) inside a try 
    static async Task _5_TaskAwait() 
    { 
    await ThrowAsync(); 
    } 

    // Helper that sets an exception asnychronously 
    static Task ThrowAsync() 
    { 
    TaskCompletionSource tcs = new TaskCompletionSource(); 
    ThreadPool.QueueUserWorkItem(_ => tcs.SetException(new Exception("ThrowAsync"))); 
    return tcs.Task; 
    } 
    internal static void Log(string message, [CallerMemberName] string caller = "") 
    { 
    Console.WriteLine("{0}: {1}", caller, message); 
    } 
} 

}

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