ConcurrentBag.ToObservable()运行一次并提前完成

时间:2014-09-18 01:04:37

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

我有一个静态集合,比如调用远程休息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"));
}

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

  

++

     

完成

1 个答案:

答案 0 :(得分:4)

如果它像那样工作会很有趣......但遗憾的是它并没有。 ToObservable扩展方法是在IEnumerable<T>界面上定义的 - 因此它可以获得该集合的时间点快照。

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

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

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

然后你可以使用你的Produce方法构造一个observable来将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的冗长解释。