2015-08-13 20 views
5

Khi trải nghiệm async mới & Đang chờ các tính năng của 4.5 Tôi muốn xóa một số nhầm lẫn trước khi tiến xa hơn nữa. Tôi đã đọc bài viết khác nhau và cũng câu hỏi khác nhau về SO và nó giúp tôi thực hiện Async và Await hoạt động như thế nào. Tôi sẽ cố gắng đặt sự hiểu biết và sự nhầm lẫn của tôi ở đây và sẽ đánh giá cao nếu một người nào đó dạy tôi và những người khác đang tìm kiếm những thứ giống nhau. Tôi đang thảo luận điều này bằng những từ ngữ rất đơn giản.Async Await Few Confusions

Vì vậy, Async được sử dụng để trình biên dịch biết rằng phương thức được đánh dấu bằng Async chứa hoạt động Await (Hoạt động dài). Khung công tác mới nhất chứa các phương thức dựng sẵn mới cho các hoạt động Async.

Hàm dựng sẵn Không đồng bộ như kết nối.OpenAsync, ExecuteScalarAsync v.v. được sử dụng với Từ khóa chờ. Tôi không biết làm việc bên trong của những phương pháp Async này nhưng dự đoán mạnh mẽ của tôi là dưới mui xe họ đang sử dụng Nhiệm vụ.

Tôi có thể đặt quy tắc này là quy tắc chung mà Await sẽ có với bất kỳ phương pháp nào triển khai Tác vụ hay không. Vì vậy, nếu tôi cần phải tạo ra phương pháp riêng của tôi đó là thực hiện hoạt động lâu sau đó tôi sẽ tạo ra nó như là nhiệm vụ và khi nó được gọi là tôi sẽ sử dụng Await từ khoá với nó?

Điều quan trọng thứ hai là quy tắc ngón tay cái của việc tạo phương thức là Không đồng bộ hoặc tạo nó làm nhiệm vụ. Ví dụ:

public void SampleMain() 
{ 
    for (int i = 1; i <= 100; i++) 
    { 
     DataTable dt = ReadData(int id); 
    } 
} 

public DataTable ReadData(int id) 
{ 
    DataTable resultDT = new DataTable(); 

    DataTable dt1 = new DataTable(); 
    // Do Operation to Fill DataTable from first connection string 
    adapter.Fill(dt1); 

    DataTable dt2 = new DataTable(); 
    // Do Operation to Fill DataTable from first connection string 
    adapter.Fill(dt2); 

    // Code for combining datatable and returning the resulting datatable 
    // Combine DataTables 
    return resultDT; 
} 

public string GetPrimaryConnectionString() 
{ 
    // Retrieve connection string from some file io operations 
    return "some primary connection string"; 
} 

public string GetSecondaryConnectionString() 
{ 
    // Retrieve connection string from some file io operations 
    return "some secondaryconnection string"; 
} 

Đây là một trường hợp rất đơn giản mà tôi đã tạo dựa trên một số ứng dụng thực tế mà tôi đã làm trước đây. Vì vậy, tôi đã chỉ tự hỏi làm thế nào để làm cho toàn bộ quá trình này Async.

Tôi có nên đặt GetPrimaryConnectionString và GetSecondaryConnectionString thành Công việc và đợi chúng trong ReadData không. ReadData cũng sẽ là một nhiệm vụ? Làm thế nào để gọi ReadData trong chức năng SampleMain?

Một cách khác có thể là tạo Tác vụ cho ReadData trong SampleMain và chạy Tác vụ đó và bỏ qua chuyển đổi các phương thức khác làm Tác vụ. Đây có phải là cách tiếp cận tốt không? Nó sẽ thực sự không đồng bộ?

+0

Mẫu ý bạn là "tạo phương thức làm nhiệm vụ" sẽ hữu ích hơn so với mã đồng bộ ngẫu nhiên một phần mà bạn đã thêm vào bài đăng. –

+0

@Alexei Levenkov Cảm ơn bạn đã trả lời. Tôi hoàn toàn không hiểu bình luận của bạn. Những gì tôi đã trình bày là một kịch bản nguyên mẫu với ít mã nhất có thể để làm cho độc giả hiểu cách làm cho toàn bộ quá trình này là Không đồng bộ bằng cách sử dụng Async và Await. Nó không phải là ngẫu nhiên cả. –

+0

@ M.kazem Akhgary. Ổn thỏa. Vì vậy, không cần phải thay đổi phương thức GetPrimaryConnectionString và GetSecondaryConenctionString. Nếu tôi chỉ làm cho ReadData là Async thì tất cả quy trình này sẽ là Không đồng bộ? –

Trả lời

6

Vì vậy Async được sử dụng để biên dịch mà biết rằng phương pháp đánh dấu bằng Async chứa hoạt động đang chờ đợi

Các async được sử dụng để trình biên dịch sẽ có một dấu hiệu để tạo ra một nhà máy ra khỏi phương pháp. Phương thức async có thể không có await và vẫn hoạt động, mặc dù nó sẽ thực thi hoàn toàn đồng bộ.

Hàm dựng sẵn Các chức năng không đồng bộ như kết nối.OpenAsync, ExecuteScalarAsync v.v ... được sử dụng với Từ khóa chờ. Tôi không biết hoạt động bên trong của các phương pháp Async này là gì, nhưng dự đoán mạnh mẽ của tôi là dưới mui xe họ đang sử dụng Công việc.

Task là lời hứa về công việc sẽ được hoàn thành trong tương lai. Có một số cách để tạo một Task. Nhưng, Task không phải là thứ duy nhất có thể đại diện cho một hoạt động không đồng bộ. Bạn có thể tự tạo một bản thân nếu bạn muốn, tất cả nó cần nó để thực hiện phương thức GetAwaiter trả về một loại thực hiện INotifyCompletion.

Nếu bạn muốn biết cách triển khai phương pháp trong khuôn khổ, you can view the source.Trong trường hợp cụ thể này, họ sử dụng TaskCompletionSource<T>.

Tôi có nên đặt GetPrimaryConnectionString và GetSecondaryConnectionString làm nhiệm vụ và đợi chúng trong ReadData không. Liệu ReadData cũng là một nhiệm vụ? Làm thế nào để gọi ReadData trong chức năng SampleMain ?

Không có gì vốn không đồng bộ về việc truy xuất chuỗi kết nối. Bạn thường (không phải lúc nào) sử dụng async-await với các hoạt động IO không đồng bộ tự nhiên. Trong trường hợp cụ thể này, thao tác không đồng bộ thực tế duy nhất là ReadData và nếu bạn muốn làm cho nó không đồng bộ, bạn có thể sử dụng SqlDataReader, hiển thị các phương thức không đồng bộ.

Một ví dụ, lấy từ ADO.NET teams blog:

public static async Task<Product> GetProductAndReviewsAsync(
      int productID, int reviewsToGet) 

{ 
    using (SqlConnection connection = new SqlConnection(ConnectionString)) 
    { 
     await connection.OpenAsync(); 
     const string commandString = GetProductByIdCommand + ";" 
            + GetProductReviewsPagedById; 

     using (SqlCommand command = new SqlCommand(commandString, connection)) 
     { 
      command.Parameters.AddWithValue("productid", productID); 
      command.Parameters.AddWithValue("reviewStart", 0); 
      command.Parameters.AddWithValue("reviewCount", reviewsToGet); 
      using (SqlDataReader reader = await command.ExecuteReaderAsync()) 
      { 
       if (await reader.ReadAsync()) 
       { 
        Product product = GetProductFromReader(reader, productID); 
        if (await reader.NextResultAsync()) 
        { 
         List<Review> allReviews = new List<Review>(); 
         while (await reader.ReadAsync()) 

         { 
          Review review = GetReviewFromReader(reader); 
          allReviews.Add(review); 
         } 
         product.Reviews = allReviews.AsReadOnly(); 
         return product; 
        } 
        else 
        { 
         throw new InvalidOperationException(
          "Query to server failed to return list of reviews"); 
        } 
       } 
       else 
       { 
        return null; 
       } 
      } 
     } 
    } 
} 
+0

Đối với tôi câu trả lời giờ đây hoàn hảo với phiên bản mã mẫu. Cảm ơn –

+0

Trong mã mẫu mà bạn đã chia sẻ với tôi, một đối tượng không có giá trị rõ ràng được đề cập bên trong tác vụ như Task . Điều này có cần phải rõ ràng không? –

+0

@AdnanYaseen Bất kỳ kiểu trả về nào từ một hoạt động không đồng bộ cần trả về một 'Task' hoặc' Task '. Tôi đã xóa 'Sản phẩm? 'Để không nhầm lẫn bạn. –

1

làm thế nào để làm cho toàn bộ quá trình này Async

Nếu có phiên bản không đồng bộ của adapter.Fill, sau đó chỉ cần await cho nó trong ReadData, mà lần lượt cũng trở thành async và bạn có thể chờ nó trong phương thức người gọi:

// in async button click event 
button.Enabled = false; 
var dt = await ReadData(int id); 
button.Enabled = true; 
... // do something with dt 

public async Task<DataTable> ReadData(int id) 
{ 
    ... 
    var job1 = adapter.AsyncFill(dt1); 
    var job2 = adapter.Fill(dt2); 
    // wait for all of them to finish 
    Task.WaitAll(new[] {job1, job2}); 
    ... 
    return Task.FromResult(resultDT); // dump approach 
} 

Nếu không có phiên bản không đồng bộ sau đó bạn phải tạo ra chúng (bằng cách sử dụng Task):

// in async button click event 
button.Enabled = false; 
// run synchronous task asynchronously 
var dt = await Task.Run(() => ReadData(int id)); 
button.Enabled = true; 
... // do something with dt 

async/await tỏa sáng khi nói đến giao diện người dùng, nếu không (nếu không có giao diện người dùng là có liên quan) chỉ cần tạo nhiệm vụ và chạy hoạt động đồng bộ ở đó.

+0

'async-await' không có liên quan gì đến giao diện người dùng. Nó * giúp * để sử dụng nó khi bạn có các hoạt động IO không chặn. Nhưng sử dụng 'Task.Run' như thế sẽ không mở rộng. –

+0

@Sinatr Cảm ơn bạn đã cung cấp cho tôi mã mẫu giúp tôi hiểu rõ hơn. Chỉ là một câu hỏi nhanh.Bạn đã đề cập rằng nếu giao diện người dùng không được tham gia thì tôi phải tạo nhiệm vụ và chạy đồng bộ. Điều gì sẽ xảy ra nếu tôi không có giao diện người dùng và phải chạy các tác vụ song song? –

+0

@AdnanYaseen, tôi không nghĩ rằng bạn sẽ đạt được bất cứ điều gì nếu bạn mở ra rằng 'cho' vào TPL. Nút cổ chai là HDD hoặc mạng. Nếu bạn làm một số xử lý nặng (có thể so sánh với thời gian cần thiết để nhận dữ liệu), thì chắc chắn nó sẽ giúp ích. Bản thân tôi chưa bao giờ phải thực hiện các tính toán như vậy, do đó câu trả lời của tôi là liên quan đến giao diện người dùng. – Sinatr

0

Lý do duy nhất để sử dụng async-await là nếu chủ đề chính của bạn có thể làm điều gì đó hữu ích trong khi một luồng khác đang thực hiện thao tác dài. Nếu thread chính sẽ bắt đầu thread khác và chỉ chờ cho thread khác kết thúc, tốt hơn là để cho thread chính thực hiện hành động.

Một trong những điều mà chuỗi chính thường làm là giữ cho giao diện người dùng phản hồi.

Bạn đang ở bên phải, dưới chế độ chờ đợi không đồng bộ hóa, sử dụng Tác vụ, do đó bạn thấy rằng hàm async trả về một Tác vụ.

Các quy tắc:

  • Nếu một hàm sẽ trả về void, phiên bản async trả Task. Nếu hàm sẽ trả về TResult, phiên bản không đồng bộ sẽ trả về Tác vụ <TResult>.
  • Có một ngoại lệ: trình xử lý sự kiện async trả về void.
  • Giá trị trả về đang đợi Tác vụ bị vô hiệu. Giá trị trả về đang đợi Nhiệm vụ <TResult> là TResult.
  • Chỉ các chức năng không đồng bộ mới có thể gọi các chức năng không đồng bộ khác.
  • Nếu bạn có chức năng không đồng bộ, bạn vẫn có thể sử dụng chức năng không đồng bộ. Tuy nhiên bạn không thể sử dụng đang chờ đợi. Sử dụng giá trị trả về Tác vụ của hàm async và các phương thức System.Threading.Tasks.Task để đợi kết quả.
  • Nếu bạn có một chức năng async và muốn bắt đầu một hàm phi async trong một thread riêng biệt, sử dụng:

    private int SlowCalculation (int a, int b) { System.Threading.Thread.Sleep (TimeSpan.FromSeconds (5)); trả lại dấu + b; }

    tin async công tác CalculateAsync (int a, int b) { công tác myTask = Task.Run (() => SlowCalculation (a, b); // khi SlowCalcuation được tính toán chậm, làm hữu ích khác điều // sau một thời gian bạn cần câu trả lời int sum = chờ đợi myTask; trở lại sum;. }

thấy rằng sự trở lại của chờ đợi công tác <int> là int

Một số người sử dụng để sử dụng các chức năng như Task.ContinueWith. Bởi vì tuyên bố chờ đợi không cần thiết nữa. Chờ đợi đảm bảo rằng nhiệm vụ được hoàn thành. Tuyên bố sau khi chờ đợi là những gì bạn thường làm trong ContinueWith.

Trong Task.ContinueVới bạn có thể nói: "chỉ thực hiện tác vụ này nếu tác vụ thất bại". Tương đương async-await cho việc này là try-catch.

Hãy nhớ rằng: nếu chủ đề của bạn không có gì hữu ích để làm (như giữ UI của bạn đáp ứng), không sử dụng async-chờ đợi

Bắt đầu từ một số nhiệm vụ trong async-chờ đợi và chờ đợi cho họ để kết thúc được thực hiện như sau:

private async Task MyAsyncFunction(...) 
{ 
    var tasks = new List<Task<int>>(); 
    for (int i=0; i<10; ++i) 
    { 
     tasks.Add(CalculateAsync(i, 2*i); 
    } 
    // while all ten tasks are slowly calculating do something useful 
    // after a while you need the answer, await for all tasks to complete: 
    await Task.WhenAll(tasks); 
    // the result is in Task.Result: 
    if (task[3].Result < 5) {...} 
} 

phiên bản async-chờ đợi của Task.Waitall là Task.WhenAll. WhenAll trả về một Task thay vì void, vì vậy bạn có thể chờ đợi nó. Các chủ đề chính vẫn đáp ứng ngay cả trong khi chờ đợi.

Chủ đề chính không phải là trường hợp khi sử dụng Task.WaitAll, bởi vì bạn không chờ đợi.