如何不冻结UI线程?

本文关键字:线程 UI 冻结 何不 | 更新日期: 2023-09-27 18:15:05

我不明白…我遵循了几个不同的指示,我发现到处都是,但我找不到如何不冻结UI线程,同时执行一些任务。

我在窗口加载时运行这个。在主窗口的窗口根。我有:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="ContentRendered">
        <i:InvokeCommandAction Command="{Binding WindowLoaded}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

触发以下内容:

private void WindowLoadedEx(object p)
{
    DBMan();
    LoadValues();
    //Boot = false;
    //MainMenuVisibility = true;
}

DBMan()是可以忽略的,问题不在这里-它是在LoadValues()。我已经修改了很多,尝试了各种不同的东西,没有工作。

private void LoadValues()
{
    //ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes);
    //Thread AddHeroesThread = new Thread(AddHeroesRef);
    //AddHeroesThread.Start();
    ////HeroesDBAddHeroes();
    //ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments);
    //Thread AddCommentsThread = new Thread(AddCommentsRef);
    //AddCommentsThread.Start();
    ////HeroesDBAddComments();
    ThreadStart LoadValuesRef = new ThreadStart(LoadValuesThreaded);
    Thread LoadValuesThread = new Thread(LoadValuesRef);
    LoadValuesThread.Start();
    //HeroesDBAssignComments();
    //SetDBIDs();
}
private async void LoadValuesThreaded()
{
    ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes);
    Thread AddHeroesThread = new Thread(AddHeroesRef);
    AddHeroesThread.Start();
    //HeroesDBAddHeroes();
    ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments);
    Thread AddCommentsThread = new Thread(AddCommentsRef);
    AddCommentsThread.Start();
    //HeroesDBAddComments();
    while (AddHeroesThread.IsAlive || AddCommentsThread.IsAlive)
    {
        Thread.Sleep(1);
    }
    HeroesDBAssignComments();
    SetDBIDs();
    Boot = false;
    MainMenuVisibility = true;
}

(正如你所看到的,我试过改变不同的东西,但都不能奏效)

基本上,HeroesDBAddHeroes()和HeroesDBAddComments()需要一起运行,它们之间没有冲突等。我将来还会在这里运行其他方法,同时在单独的线程中运行。

但是,我需要等待这些都完成,然后再继续并允许HeroesDBAssignComments()运行。如果以上的一切都还没有完成,它就不会起作用。然后,我需要改变布尔值Boot和MainMenuVisibility一旦HeroesDBAssignComments完成。

如何不冻结UI线程?

既然你没有指定。net版本,我就建议用4.0的方式来处理这个问题:

void BlockingMethod() {
    Task.Factory.StartNew(() => {
        // blocking work here, db loading for example
    })
    .ContinueWith(result => {
        // update control here with the result of result.Result
       },
       TaskScheduler.FromCurrentSynchronizationContext()
    });
}

您可能需要稍微修改一下语法。

这是做什么的?

基本上当BlockingMethod被调用时,它将启动一个异步任务。在你的例子中,这个方法将被UI调用,这就是为什么你的UI冻结了。

因为我们是异步的,所以当方法已经离开时,我们传递给执行的代码将仍然运行,因为它的工作是由框架处理的。

一旦重载完成,"ContinueWith"中的代码将被执行。我们正在传递TaskScheduler.FromCurrentSynchronizationContext(),因为我们需要确保这段代码只在方法最初被调用的上下文中执行(ui sync上下文),否则你会得到一个异常,因为wpf不允许这样做。

使用基于任务的异步模式(TAP)使此类问题变得非常简单。

顶层方法WindowLoadedEx可以声明为async void,因为它基本上是一个美化的事件处理程序。然后,您可以使用async/await和捕获的WPF同步上下文在正确的线程上发布UI更改,即:

private async void WindowLoadedEx(object p)
{
    DBMan();
    await LoadValuesAsync();
    Boot = false;
    MainMenuVisibility = true;
}
private async Task LoadValuesAsync()
{
    var addHeroesTask = HeroesDBAddHeroesAsync();
    var addCommentsTask = HeroesDBAddCommentsAsync();
    // We cannot assign comments or IDs until these have been added
    await Task.WhenAll(addHeroesTask, addCommentsTask).ConfigureAwait(false);
    // TODO Should these both also be asynchronous?
    HeroesDBAssignComments();
    SetDBIDs();
}

注意我使用了HeroesDBAddHeroesAsyncHeroesDBAddCommentsAsync,因为我相信它们是真正的异步数据库操作。

你不应该使用Thread.Sleep()来等待。请使用WaitHandle。

使用WaitHandle的基本方法是在你想等待的地方调用WaitOne(),然后在另一个线程上执行工作,然后在WaitHandle上调用Set()。

我通常使用eventwaithhandle