2016-03-17 61 views
1

我相信我下面有比賽條件。我手動構建一個帶有JSON輸出的HttpResponseMessage以異步流式傳輸。這個問題似乎與櫃檯(我)。我想在第一次寫入列表之後的任何元素之前添加逗號。異步/等待比賽條件

在列表的開頭,有時第一次寫入後的第一對記錄(我見過最多3個)不會有前面的逗號。數字不一致,有時按預期工作。我沒有在我的本地機器上看到它,但是在部署的環境中使用了更強大的硬件。

var LastUpdate = JsonConvert.SerializeObject(dt); 
var pre = $"{{ \"LastUpdate\": {LastUpdate}, \"List\":["; 
var post = "]}"; 

HttpResponseMessage response = Request.CreateResponse(); 
response.Content = new PushStreamContent(
    async (stream, http, context) => 
    { 
     try 
     { 
      int i = 0; 
      var buffer = Encoding.UTF8.GetBytes(pre); 
      await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); 

      var query = getQuery(id);        
      await query 
       .ForEachAsync(async entity => 
       { 
        var student = MapRecord(entity); 
        if (student != null) 
        { 
         var json = JsonConvert.SerializeObject(student); 
         buffer = Encoding.UTF8.GetBytes(((i > 0) ? ", " : "") + json); 
         await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); 
         i++; 
        } 
       }, cancellationToken).ConfigureAwait(false); 

      buffer = Encoding.UTF8.GetBytes(post); 
      await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); 
     } 
+0

如何'ForEachAsync'聲明? –

+0

@YuvalItzchakov也許OP使用[QueryableExtensions.ForEachAsync](https://msdn.microsoft.com/en-us/library/system.data.entity.queryableextensions.foreachasync(v = vs.113).aspx) – juharr

回答

4

如果您使用QueryableExtentions.ForEachAsync(感謝@juharr),那麼,你有一個競爭條件。

該方法的簽名是:

public static Task ForEachAsync<T>(
    this IQueryable<T> source, 
    Action<T> action 
) 

注意,方法接受Action<T>。在異步世界中,這與async void等效。這意味着每次你在異步委託裏面await時,迭代器實際上都會延續到下一個元素,而不是等待你的委託完成。

相反(如果查詢不是一個非常大的數據集調用),使用常規foreach聲明和await裏面:

foreach (var entity in query) 
{ 
    var student = MapRecord(entity); 
    if (student != null) 
    { 
     var json = JsonConvert.SerializeObject(student); 
     buffer = Encoding.UTF8.GetBytes(((i > 0) ? ", " : "") + json); 
     await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken) 
        .ConfigureAwait(false); 
     i++; 
    } 
} 
+0

使用foreachasync來防止數據庫級別的阻塞(該查詢返回〜100萬行),還有其他關於同步計數器的建議? – ajberry

+0

如果是100萬行,「foreach」可能不是最好的主意。你是否有任何理由手動進行序列化?如果這是不可避免的,我會推薦使用同步委託,並使用'stream.Write'而不是'stream.WriteAsync'。或者更好的是,爲EF創建一個pull請求,並在'ForEachAsync'內添加一個用於調用異步委託的選項。 –

+0

將整個結果集放入映射和序列化命中內存異常。手動構建並不理想,但一次可以傳輸小數據。這種方法對資源的佔用非常少。 – ajberry

0

如果您關注的主要是與第(i)計數器,可以使用Interlocked.Increment來安全地增加i。這會導致少量的同步性能爭用,但您可以通過這種方式持續更新。

例子:

Interlocked.Increment(ref i); 

Interlocked.Increment MSDN