2015-09-28 126 views
0

我无法弄清楚这一点......我遵循了我在各处找到的几种不同的说明,但我无法找到如何在执行几项任务时不冻结UI线程。如何不冻结UI线程?

我在窗口加载时运行它。 在MainWindow.xaml窗口的根,我有:

<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()运行之前,我需要等待这些完成。如果上面的所有内容都不完整,它不会工作。然后,一旦HeroesDBAssignComments完成,我需要更改布尔值Boot和MainMenuVisibility。

+0

你有没有试过调用'Dispatcher.Invoke(...)'方法? –

+2

你不应该使用Thread.Sleep()来等待。改用[WaitHandle] [1]。 [1]:http://stackoverflow.com/questions/2538065/what-is-the-basic-concept-behind-waithandle – Hristo

+0

哪里做呢? – pingu2k4

回答

0

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

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

我通常使用EventWaitHandle

+0

这仍然会阻塞UI线程,冻结应用程序。 – Servy

1

既然你没有指定一个.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同步上下文)调用的上下文中执行,否则你会得到一个异常,因为wpf不允许这个。

1

使用基于任务的异步模式(TAP)使这样的问题非常简单。

您的顶级方法WindowLoadedEx可以声明为async void,因为它基本上是一个荣耀的事件处理程序。然后,你可以使用async/await与捕获的WPF同步上下文发布在正确的线程用户界面的变化,即:

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,因为我相信这是真正的异步数据库操作。

阅读关于使用async/await here