2014-10-19 159 views
1

我正在运行后台线程从数据库中获取数据,以便当前线程(UI)不冻结。我正在使用TPL来实现这一点。如何使父线程等待子线程完成 - C#

List<string> _myList1, _myList2; 
private void LoadCollections(){ 
    Task db_task = new Task(() => CallDB()); 
    var continuations = db_task.ContinueWith((ant) => 
    { 
     _myList1 = GetDataFromMetaData1(); 
     _myList2 = GetDataFromMetaData2(); 
    }); 

    db_task.Start(); 
    LoadTemplates(); //execute only after 'db_task' completes execution 
} 

在上面的代码

CallDB() - 获取从在元数据数据库中,并将其存储的数据。在这种情况下,请考虑它是一个字符串列表。 (说List<string> MetaStrings

GetDataFromMetaData1()GetDataFromMetaData2() - 获取2个单独的名单,这是MetaStrings子目录。

现在我只希望在加载_myList1_myList2之后,即db_task已经完成执行之后,执行函数LoadTemplates。目前,我正在使用这个

while(_mylist1==null && _mylist2==null) 
    Thread.Sleep(50); 

LoadTemplates(); 

但是,当UI线程进入睡眠状态时,这显然会冻结UI。那么任何人都可以提出一种有效的方法来处理这种情况?

PS:LoadTemplates()初始化一些ObservableCollection变量。如果在子线程中调用LoadTemplates(),则db_task则引发以下错误This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread。因此产生了线程等待的情况。

+0

不能使用异步&等待? – terrybozzio 2014-10-19 23:28:32

+0

实际上'LoadCollections'函数是一个信使事件。换句话说,当用户点击UI上的一个按钮时,ViewModel注册来调用'LoadCollections'。所以在这种情况下,我有点失落了如何使用async/await。 – SohamC 2014-10-19 23:32:10

+0

不能使用Task.WaitAll(任务); – user1274820 2014-10-20 00:04:18

回答

5

你在问错误的问题。在辅助线程上运行进程时,您绝不希望主线程等待以完成操作。这只是一个浪费&hellip;你也可能只是在主线程上运行这个操作并完成它。

所以相反,问题是:我如何让主(父)线程处理辅助(子)线程的完成?

根据您得到的错误消息,看起来您正在使用WPF编写此代码。这意味着询问的问题的一个答案是,您可以从响应完成辅助线程的事件处理程序中调用Dispatcher.Invoke。

正如Terry所建议的那样,您可以使用异步/等待功能。例如,这威力工作(很难不完整的代码示例说):

List<string> _myList1, _myList2; 
private async void LoadCollections(){ 
    Task db_task = new Task(() => CallDB()); 

    var continuations = db_task.ContinueWith((ant) => 
    { 
     _myList1 = GetDataFromMetaData1(); 
     _myList2 = GetDataFromMetaData2(); 
    }); 

    db_task.Start(); 

    await continuations; 

    LoadTemplates(); //execute only after 'db_task' completes execution 
} 

换句话说,继续前进,因为你现在做安排您的工作,但使用异步和等待设置因此LoadTemplates()只有在继续完成后才会被调用(这反过来在初始数据库任务完成之前不会被执行),特别是在主Dispatcher线程中执行LoadTemplates() LoadCollections首先被调用。

请注意,理想情况下,LoadCollections的返回类型实际上是Task。编译器更喜欢这一点,它可以让你更好地处理可能出现的异常。但是void应该没问题,并且在处理事件处理函数方法时并不少见。

编辑:我也意味着提,涉及更彻底改变代码的替代但恕我直言,是更符合异步/ AWAIT模式保持是,要么简单地等待原db_task然后执行“延续”语句内联(即不在任务中),或者(如果这些语句本身是长时间运行的并且应该在后台执行)仍然等待原始任务,然后还等待继续作为他们自己的任务,而不是显式地作为数据库任务。

例如(假设延续操作简单,并且可以在主线程上运行):

List<string> _myList1, _myList2; 
private async void LoadCollections(){ 
    await Task.Run(() => CallDB()); 

    _myList1 = GetDataFromMetaData1(); 
    _myList2 = GetDataFromMetaData2(); 

    LoadTemplates(); //execute only after 'db_task' completes execution 
} 
+0

我正在尝试使用'Dispatcher.Invoke'来解决你的问题,'Dispatcher.CurrentDispatcher.Invoke(LoadTemplates,DispatcherPriority.ContextIdle);'。但这似乎并不奏效。你是否知道如何使Dispatcher.Invoke响应完成的辅助线程。 – SohamC 2014-10-20 07:25:58

+0

首先,定义“似乎不起作用”。这可能是很多不同的事情。其次,为什么使用ContextIdle作为优先级?我的意思是,这应该可以工作,但如何开始与正常,然后从那里去。第三,虽然使用分派器应该工作,异步/等待是恕我直言,更简单,并产生更清晰的代码。你是否尝试过这种方式呢? – 2014-10-20 08:04:49

+0

解决方案运行。谢谢!!这并不重要,但我收到一条警告消息:'因为这个调用没有被等待,所以在调用完成之前继续执行当前的方法。考虑将“await”操作符应用于调用的结果。“你知道它意味着什么! – SohamC 2014-10-22 07:04:37

0

如果我理解你正确地质疑CallDB()必须先运行,那么GetDataFromMetaData功能可以运行,然后最后LoadTemplates。我会使用ansy等待如下:

private async void LoadCollections() 
    { 

     await Task.Run(() => CallDB()); 

     Task[] tasks = new Task[1] 
     { 
      _myList1 = GetDataFromMetaData1(), 
      _myList2 = GetDataFromMetaData2() 
     }; 

     await Task.WhenAll(tasks); 

     LoadTemplates(); //execute only after 'db_task' completes execution 

    }