在循环中等待两件事?

时间:2016-01-25 01:41:08

标签: c# asynchronous task

我如何同时等待两件或更多件事(不同类型)?就像在事件循环中一样:

while(true) {
    Letter msg1 = await WaitForLetter();
    //read msg1 and reply.
    SMS msg2 = await WaitForSMS();
    //read msg2 and reply
}

看起来并不正确。这两条消息最终会相互阻塞?

2 个答案:

答案 0 :(得分:4)

我认为您最好的选择是使用Microsoft的Reactive Framework(NuGet“Rx-Main”)。它与Tasks非常吻合。

以下是您需要的代码:

var subscription1 =
    Observable
        .FromAsync(WaitForLetter)
        .Repeat()
        .Subscribe(msg1 =>
        {
            //read msg1 and reply.
        });

var subscription2 =
    Observable
        .FromAsync(WaitForSMS)
        .Repeat()
        .Subscribe(msg2 =>
        {
            //read msg2 and reply
        });

两者彼此独立运行,并且都是异步运行。

要阻止它们运行,只需执行以下操作:

subscription1.Dispose();
subscription2.Dispose();

如果你真的希望它们像事件循环那样运行,那么消息都在同一个线程上,相互穿插,那么你可以这样做:

var eventLoopScheduler = new EventLoopScheduler();

var subscription1 =
    Observable
        .FromAsync(WaitForLetter)
        .Repeat()
        .ObserveOn(eventLoopScheduler)
        .Subscribe(msg1 =>
        {
            //read msg1 and reply.
        });

var subscription2 =
    Observable
        .FromAsync(WaitForSMS)
        .Repeat()
        .ObserveOn(eventLoopScheduler)
        .Subscribe(msg2 =>
        {
            //read msg2 and reply
        });

你有更多的清理工作,但这可以很好地处理:

var subscriptions = new CompositeDisposable(
    subscription1,
    subscription2,
    eventLoopScheduler);

//then later

subscriptions.Dispose();

答案 1 :(得分:3)

目前,您的代码将等待每个方法依次完成。如果要发送每条消息,然后在最后等待两个消息,则可以使用Task.WaitAll方法(假设您的方法返回Task<T>个对象。

while(true) {
    Task<Letter> msgTask1 = WaitForLetter();
    Task<SMS> msgTask2 = WaitForSMS();

    Task.WaitAll(msgTask1, msgTask2);
}

然后,您可以使用Result属性获取每项任务的结果(再次假设您的方法返回Task<T>

Letter msg1 = msgTask1.Result;
SMS msg2 = msgTask2.Result;

当然,这一切都假设WaitForLetterWaitForSMS的实施是独立的,不会相互阻挡。

如果您只想等待任何任务完成,您可以将`Task.WaitAny'用于同一目的。这将返回已完成的任务的索引,以便您知道哪一个已完成。

while(true) {
    Task<Letter> msgTask1 = WaitForLetter();
    Task<SMS> msgTask2 = WaitForSMS();

    var finishedTask = Task.WaitAny(msgTask1, msgTask2);
}