【问题标题】:ConcurrentBag.ToObservable() runs once and completes prematurelyConcurrentBag.ToObservable() 运行一次并过早完成
【发布时间】:2014-09-18 16:58:53
【问题描述】:

我有一个静态集合,比如调用远程休息 api 的任务:

static ConcurrentBag<Task<HttpResponseMessage>> _collection = new ConcurrentBag<Task<HttpResponseMessage>>();

static void Main(string[] args)
{
    Task.Factory.StartNew(() => Produce());
    Task.Factory.StartNew(() => Consume());

    Console.ReadKey();
}

一个线程向其中添加新项目:

private static void Produce()
{
    while (true)
    {
        var task = HttpClientFactory.Create().GetAsync("http://example.com");
        _collection.Add(task);
        Thread.Sleep(500);
    }
}

另一个线程应该处理这些项目:

private static void Consume()
{
    _collection.ToObservable()
               .Subscribe(
                   t => Console.WriteLine("++"),
                   ex => Console.WriteLine(ex.Message),
                   () => Console.WriteLine("Done"));
}

但它只运行一次并且过早完成。所以输出是;

++

完成

【问题讨论】:

  • 您还期待什么?
  • @MarcinJuraszek:长话短说,我希望它能够工作。

标签: c# task-parallel-library system.reactive


【解决方案1】:

如果它像那样工作会很有趣......但遗憾的是它没有。 ToObservable 扩展方法是在 IEnumerable&lt;T&gt; 接口上定义的 - 所以它会获取集合的时间点快照。

您需要一个可以观察到的集合,例如ObservableCollection。有了这个,您可以响应添加事件以提供 Rx 管道(可能通过将CollectionChanged 事件与Observable.FromEventPattern 连接起来)。请记住,此集合不支持并发添加。这种技术是“进入 monad”的一种方式(即获得IObservable&lt;T&gt;)。

等效是将您的请求负载添加到主题。无论哪种方式,您都可以将它们投影到异步请求中。所以说(为了争论),你的 Produce 签名看起来像这样:

private static async Task<HttpResponseMessage> Produce(string requestUrl)

然后你可以构造一个 observable 来使用你的 Produce 方法将 requestUrls 转换为异步 Web 请求,如下所示:

var requests = new Subject<string>();
var responses = requests.SelectMany(
    x => Observable.FromAsync(() => Produce(x)));

responses.Subscribe(
               t => Console.WriteLine("++"),
               ex => Console.WriteLine(ex.Message),
               () => Console.WriteLine("Done"));

并使用以下内容提交每个请求:

requests.OnNext("http://myurl");

如果您需要并发添加,请参阅Observable.Synchronize

如果您需要控制处理响应的线程,请使用ObserveOn,我对here 进行了冗长的解释。

【讨论】:

  • 添加了关于ObserveOn的注释
  • 太棒了!感谢您对时间点快照的解释。这绝对是有道理的,我所观察到的就是秒。
  • 这里可以使用Task&lt;HttpResponseMessage&gt;吗?我不能有字符串,因为它可能是 GET、POST 等请求。并且将是更通用的解决方案。我想我可以让它自己工作:)
  • 要记住的是 Task&lt;T&gt; 确实是一个不同的异步范式 - 所以你可以,但使用 ToObservable() 转换为 IObservable&lt;HttpResponseMessage&gt; 可能是值得的 - 在这种情况下你的主题将是一个Subject&lt;IObservable&lt;HttpResponseMessage&gt;&gt;(即流的流),可以通过调用SelectMany(x =&gt; x) 将其展平以提供HttpResponseMessage 的平面流。
  • 我很困惑。 Produce() 正在产生新的请求。所以 OnNext() 也是。那么谁在消费它们呢?谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 2018-04-27
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 2012-12-17
  • 1970-01-01
相关资源
最近更新 更多