异步/等待竞争条件

时间:2016-03-17 12:49:46

标签: c# asynchronous stream async-await task-parallel-library

我相信我的竞争条件如下。我手动构建一个带有JSON输出的HttpResponseMessage来异步流式传输。问题似乎与柜台(i)有关。我想在列表第一次写入后的任何元素之前添加一个逗号。

在列表的开头,有时第一次写入后的前几个记录(我已经看到最多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);
        }

2 个答案:

答案 0 :(得分:4)

如果你正在使用QueryableExtentions.ForEachAsync(感谢@juharr),那么是的,你有竞争条件。

该方法的签名是:

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

请注意,该方法接受Action<T>。在异步世界中,这相当于async void。这意味着每次await在异步委托中,ForEachAsync迭代器实际上都会继续到下一个元素,而不是等待代理完成。

相反(如果未在非常大的数据集上调用查询),请在其中使用常规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++;
    }
}

答案 1 :(得分:0)

如果你的问题主要在于(i)计数器,你可以使用Interlocked.Increment来线程安全地增加i。这会导致少量的同步性能争用,但您可以通过这种方式持续更新。

示例:

Interlocked.Increment(ref i);

Interlocked.Increment MSDN