【问题标题】:Using TaskCompletionSource with a queue and background thread将 TaskCompletionSource 与队列和后台线程一起使用
【发布时间】:2016-11-26 06:06:02
【问题描述】:

在我的 ASP.NET API 中,我收到了一系列要持久保存的消息。在这种特殊情况下,API 的响应能力取代了任何单个消息的重要性,因此我不是直接写入我的数据存储,而是将传入的消息推送到队列并在后台线程上尽快处理它们。

在某些情况下,我可能需要确认写入操作的成功或失败,因此我将消息包装在使用以下代码的自定义 MessageContext 对象中:

public class WriteMessageContext
{
    public Message Message { get; private set; }
    TaskCompletionSource<bool> complete = new TaskCompletionSource<bool>();

    public WriteMessageContext(Message message)
    {
        Message = message;
    }

    public Task<bool> WaitForInsert()
    {
        return complete.Task;
    }

    public void Success()
    {
        complete.SetResult(true);
    }

    public void Error()
    {
        complete.SetResult(false);
    }
}

Success 和 Error 方法旨在由在后台处理每个排队消息的工作线程调用。然后,在我的控制器中,我可以这样做:

var ctx = new WriteMessageContext(msg);
queue.Enqueue(ctx);

// optionally...
if (await ctx.WaitForIt()) {
    // successful
} else {
    // or not
}

这是对 TaskCompletionSource + async/await 的适当使用,还是我毫不犹豫地放弃了它?这样做有什么潜在的问题我应该担心吗?

【问题讨论】:

  • 我建议使用 TaskCreationOptions.RunContinuationsAsynchronouslyTaskCreationOptions.DenyChildAttach 标志。

标签: c# .net async-await


【解决方案1】:

对我来说,这是TaskCompletionSource&lt;T&gt; 的有效用法。

顺便说一句,也许您会感到惊讶,因为 Task Parallel Library (TPL) 中存在一些隐藏的功能/宝石,称为 data-flow blocks

其实有一个可以满足你的要求:BufferBlock&lt;T&gt;:

BufferBlock&lt;T&gt; 类表示通用异步消息传递结构。此类存储可以由多个源写入或由多个目标读取的消息的先进先出 (FIFO) 队列。当目标接收到来自BufferBlock&lt;T&gt; 对象的消息时,该消息将从消息队列中删除。因此,虽然BufferBlock&lt;T&gt; 对象可以有多个目标,但只有一个目标会收到每条消息。当您想将多条消息传递给另一个组件并且该组件必须接收每条消息时,BufferBlock&lt;T&gt; 类很有用。

【讨论】:

  • 甚至不知道它的存在。我可能应该使用它,而不是像现在这样轮询 ConcurrentQueue
  • @Chris 是的,它不是 .net 中最受欢迎的功能,但我们仍然可以使用它;)
猜你喜欢
  • 1970-01-01
  • 2018-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多