我正在运行后台线程来从数据库中获取数据,以便当前线程(UI)不会冻结.我正在使用TPL来实现这一目标.
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
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中
CallDB() - 从数据库中获取数据并将其存储在元数据中.在这种情况下,请将其视为字符串列表.(比如说List<string> MetaStrings)
GetDataFromMetaData1()和GetDataFromMetaData2()- 获取2个单独的列表,这些列表是子列表MetaStrings.
现在我想执行的功能LoadTemplates后,才_myList1和_myList2被加载时,即db_task执行完毕.目前,我正在使用它
while(_mylist1==null && _mylist2==null)
Thread.Sleep(50);
LoadTemplates();
Run Code Online (Sandbox Code Playgroud)
但是当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.因此出现了线程等待的场景.
你问的是错误的问题.在辅助线程上运行进程时,您永远不希望主线程等待操作完成.这只是浪费......你可能只是在主线程上运行操作并完成了它.
所以相反,问题是:如何让主(父)线程处理辅助(子)线程的完成?
根据您收到的错误消息,您似乎正在使用WPF编写此消息.这意味着您应该问的问题的一个答案是,您从响应辅助线程完成的事件处理程序调用Dispatcher.Invoke.
正如Terry建议的那样,您可以使用async/await功能.例如,这可能有效(很难说没有完整的代码示例):
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
}
Run Code Online (Sandbox Code Playgroud)
换句话说,继续按照现在的步骤安排你的任务,但是使用async和await进行设置,这样只有在继续完成后才会调用LoadTemplates()(直到初始数据库任务已完成),特别是,在主Dispatcher线程中执行LoadTemplates(),首先调用LoadCollections.
请注意,理想情况下,LoadCollections的返回类型实际上是Task.编译器更喜欢这个,它可以让你更好地处理可能出现的异常.但是void应该没问题,并且在处理事件处理程序方法时并不罕见.
编辑:我还想提一下,一个替代方案涉及更大幅度地更改代码但是恕我直言的更符合async/await模式将是简单地等待原始db_task然后内联执行"continuations"语句(即不在任务中),或者(如果这些语句本身长时间运行并且应该在后台执行)仍然等待原始任务,然后等待继续作为他们自己的任务而不是显式地作为数据库任务的延续.
例如(假设延续操作很简短,可以在主线程上运行):
List<string> _myList1, _myList2;
private async void LoadCollections(){
await Task.Run(() => CallDB());
_myList1 = GetDataFromMetaData1();
_myList2 = GetDataFromMetaData2();
LoadTemplates(); //execute only after 'db_task' completes execution
}
Run Code Online (Sandbox Code Playgroud)