2013-10-27 18 views
7

Làm việc với thư viện Azure Storage Client 2.1, tôi đang làm việc để tạo truy vấn không đồng bộ lưu trữ bảng. Tôi đã tạo mã này:Cách sử dụng Table.ExecuteQuerySegmentedAsync() với bộ lưu trữ bảng Azure

public async Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey) 
{ 
    var theQuery = _table.CreateQuery<TAzureTableEntity>() 
         .Where(tEnt => tEnt.PartitionKey == partitionKey); 
    TableQuerySegment<TAzureTableEntity> querySegment = null; 
    var returnList = new List<TAzureTableEntity>(); 
    while(querySegment == null || querySegment.ContinuationToken != null) 
    { 
     querySegment = await theQuery.AsTableQuery() 
            .ExecuteSegmentedAsync(querySegment != null ? 
             querySegment.ContinuationToken : null); 
     returnList.AddRange(querySegment); 
    } 
    return returnList; 
} 

Giả sử có một lượng lớn dữ liệu quay lại để có rất nhiều chuyến đi khứ hồi tới Bộ nhớ bảng. Vấn đề tôi gặp phải là chúng tôi đang chờ một bộ dữ liệu, thêm nó vào danh sách trong bộ nhớ, đang chờ thêm dữ liệu, thêm nó vào cùng một danh sách, đang chờ thêm dữ liệu, thêm dữ liệu vào danh sách ... trên và vân vân. Tại sao không chỉ quấn một Task.Factory.StartNew() xung quanh một TableQuery thông thường? Giống như vậy:

public async Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey) 
{ 
    var returnList = await Task.Factory.StartNew(() => 
               table.CreateQuery<TAzureTableEntity>() 
               .Where(ent => ent.PartitionKey == partitionKey) 
               .ToList()); 
    return returnList; 
} 

Làm theo cách này có vẻ như chúng tôi không bật lại SynchronizationContext quá nhiều. Hay nó thực sự quan trọng?

Edit để rephrase câu hỏi

sự khác biệt giữa hai tình huống nêu trên là gì?

+1

Bạn đặt "phân khúc" của mình trong phương pháp async riêng của nó và gọi nó bằng cách sử ConfigureAwait (sai). –

+0

@PauloMorgado - Trớ trêu thay tôi thực sự có TableQuerySegement và trong khi câu lệnh trong một phương thức riêng biệt đã có nhưng tôi không biết về phương thức ConfigureAwait (false) trên Task. Cảm ơn vì tiền hỗ trợ! – Hallmanac

+2

ConfigureAwait (false) được khuyến nghị cho tất cả mã thư viện và tôi sẽ mở rộng nó cho tất cả mã ứng dụng không phải UI. –

Trả lời

6

Sự khác biệt giữa hai là phiên bản thứ hai của bạn sẽ chặn một chuỗi ThreadPool trong suốt thời gian truy vấn đang thực thi. Điều này có thể chấp nhận được trong một ứng dụng GUI (trong đó tất cả những gì bạn muốn là thực thi mã ở đâu đó khác với chuỗi giao diện người dùng), nhưng nó sẽ phủ nhận mọi lợi thế khả năng mở rộng của async trong một ứng dụng máy chủ.

Ngoài ra, nếu bạn không muốn phiên bản đầu tiên của bạn để trở về bối cảnh giao diện người dùng cho mỗi khứ hồi (đó là một yêu cầu hợp lý), sau đó sử dụng ConfigureAwait(false) bất cứ khi nào bạn sử dụng await:

querySegment = await theQuery.AsTableQuery() 
          .ExecuteSegmentedAsync(…) 
          .ConfigureAwait(false); 

Bằng cách này, tất cả các lần lặp lại sau lần đầu tiên sẽ (rất có thể) thực thi trên một luồng ThreadPool chứ không phải trên ngữ cảnh giao diện người dùng.

BTW, trong phiên bản thứ hai của bạn, bạn không thực sự cần await ở tất cả, bạn có thể chỉ trực tiếp trả lại Task:

public Task<List<TAzureTableEntity>> GetByPartitionKey(string partitionKey) 
{ 
    return Task.Run(() => table.CreateQuery<TAzureTableEntity>() 
           .Where(ent => ent.PartitionKey == partitionKey) 
           .ToList()); 
} 
+0

Làm thế nào để các truy vấn querySegment (phiên bản đầu tiên) hỗ trợ (thay vì phủ nhận) khả năng mở rộng của async? Tất cả mọi thứ bằng nhau bạn vẫn đang chiếm một chuỗi 'ThreadPool'. Đặc biệt là khi sử dụng 'ConfigureAwait (false)'. Tôi có nghĩ đúng không? – Hallmanac

+1

@Hallmanac Không, đó là sai. Điểm "chờ đợi" là trong khi hoạt động không đồng bộ thực hiện, bạn * không * chiếm bất kỳ chủ đề nào. Với 'ConfigureAwait (false)', bạn đang sử dụng một chuỗi 'ThreadPool' chỉ trong một khoảng thời gian ngắn giữa các phân đoạn, để bắt đầu phân đoạn tiếp theo. – svick

+2

Gotcha. Đã không nhận ra rằng nó đã không chiếm một thread 'ThreadPool' trong khi chờ đợi' QuerySegment' để trở về với dữ liệu. Trớ trêu thay, trong khi đào sâu hơn về bình luận trước đây của bạn, tôi tìm thấy một câu trả lời tốt mà bạn đã đưa ra về chủ đề này. :-) Đây là liên kết trong trường hợp những người khác thấy nó hữu ích như tôi đã làm. http://stackoverflow.com/a/14898584/350312 – Hallmanac

2

Không chắc chắn đây có phải là câu trả lời bạn đang tìm kiếm hay không nhưng tôi vẫn muốn đề cập đến nó :).

Như bạn có thể đã biết, phương pháp thứ 2 (sử dụng Task) xử lý thẻ tiếp tục nội bộ và đi ra khỏi phương thức khi tất cả các thực thể đã được tìm nạp trong khi phương pháp thứ nhất tìm nạp một tập hợp các thực thể (tối đa 1000) và sau đó đưa ra cho bạn bộ kết quả cũng như mã thông báo tiếp tục.

Nếu bạn quan tâm đến việc tìm nạp tất cả các thực thể từ bảng, cả hai phương pháp đều có thể được sử dụng, tuy nhiên, phương pháp thứ nhất mang lại cho bạn sự linh hoạt khi thoát khỏi vòng lặp một cách duyên dáng bất cứ lúc nào. Vì vậy, bằng cách sử dụng chức năng đầu tiên bạn về cơ bản có thể giới thiệu khái niệm phân trang.

Giả sử bạn đang xây dựng một ứng dụng web hiển thị dữ liệu từ một bảng. Hơn nữa, hãy giả sử rằng bảng chứa số lượng lớn các thực thể (giả sử 100000 thực thể). Sử dụng phương pháp thứ nhất, bạn chỉ có thể tìm nạp 1000 thực thể trả lại kết quả cho người dùng và nếu người dùng muốn, bạn có thể tìm nạp 1000 bộ thực thể tiếp theo và hiển thị chúng cho người dùng. Bạn có thể tiếp tục làm điều đó cho đến khi người dùng muốn và có dữ liệu trong bảng. Với phương thức thứ 2, người dùng sẽ phải đợi cho đến khi tất cả 100000 thực thể được lấy từ bảng.

+0

Đây là lý do khác mà tôi đã nghĩ về điều này. Nếu tôi đã lừa/buộc phương thức trả về một IEnumerable thực thì tôi phủ nhận những lợi ích của async/await. Mặc dù trường hợp sử dụng phân trang của bạn khiến tôi suy nghĩ một chút. Bạn sẽ phải chuyển mã thông báo tiếp tục cho người tiêu dùng để họ có thể chuyển nó trở lại kho lưu trữ để nhận nơi họ rời đi. Đây sẽ là một trường hợp sử dụng tốt cho async trong ngữ cảnh này. – Hallmanac

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