使用c#异步调用的自定义消息泵送

本文关键字:自定义消息 调用 异步 使用 | 更新日期: 2023-09-27 18:00:25

我正在创建自己的UI处理逻辑,该逻辑在单个线程上运行。基本上,我正在做的是

void HandlerMain()
{
    while (true)
    {
        Task[] events = PumpEvents();
        Task.WaitAll(events);
    }
}

其中PumpEvents()返回的一个示例任务是

async Task ButtonClick()
{
    var httpClient = new HttpClient();
    string s = await httpClient.GetStringAsync("http://microsoft.com");
    Console.WriteLine(s);
}

我的代码的问题是,如果其中一个events花了很长时间,它就会卡在Task.WaitAll()上,所以它不能引发新的事件,从而使UI没有响应。除了WaitAll(),还有其他类似的方法吗

Task[] remainingEvents = PumpEvents();
while (true)
{
    remainingEvents = WaitUntilEveryTasksAwait(remainingEvents);
    remainingEvents.Append(PumpEvents());
}

也许我走错了路。我很感激你的建议!

使用c#异步调用的自定义消息泵送

@ScottChamberlain否。内置的UI处理器,比如WPF,可以正确地处理异步事件,所以每当异步事件"等待"时,它就会跳过当前上下文,处理下一个事件。我想复制这种行为

根据你对我的评论,我现在明白了你的问题是什么。你需要比简单的while(true)循环更多的逻辑才能处理await消息。整个系统是建立在类SynchronizationContext上的,您需要做的是从SynchronizationContext派生您自己的类,并覆盖它的方法,以便在while循环中排队完成您的工作。

请参阅Stephen Cleary的这篇文章,为您提供更多关于同步上下文如何工作的信息,并可能提供一些关于从哪里开始编写自己的想法。

如果我理解的话,您实际上不需要等到这些任务完成后才能继续。只需删除对Task.WaitAll的调用。

对于记录,Task.WaitAll是同步的——它会阻塞。您最好使用Task.WhenAll,它返回一个在所有提供的任务完成时完成的任务。这让你可以像等待任何一项单独的任务一样等待它。这样做可以解决问题并保持UI的响应性。

例如:

async Task HandlerMain()
{
    while (true)
    {
        Task[] events = PumpEvents();
        await Task.WhenAll(events);
    }
}

当然,在这种情况下,根据调用HandlerMain的内容,您仍然会遇到PumpEvents挂断等待长时间运行的任务的问题。

这开始引出一些问题,比如"当WPF和WinForms已经解决了这个问题时,为什么要编写自己的自定义UI消息泵?"