如何不冻结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完成。
既然你没有指定。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();
}
注意我使用了HeroesDBAddHeroesAsync
和HeroesDBAddCommentsAsync
,因为我相信它们是真正的异步数据库操作。
你不应该使用Thread.Sleep()来等待。请使用WaitHandle。
使用WaitHandle的基本方法是在你想等待的地方调用WaitOne(),然后在另一个线程上执行工作,然后在WaitHandle上调用Set()。
我通常使用eventwaithhandle