我正在运行后台线程来从数据库中获取数据,以便当前线程(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
}
在上面的代码中
CallDB()
- 从数据库中获取数据并将其存储在元数据中。在这种情况下,请将其视为字符串列表。 (比如List<string> MetaStrings
)
GetDataFromMetaData1()
和GetDataFromMetaData2()
- 获取2个单独的列表,这些列表是MetaStrings
的子列表。
现在我希望仅在加载LoadTemplates
和_myList1
之后执行函数_myList2
,即db_task
完成执行时。目前,我正在使用此
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 :(得分:5)
你问的是错误的问题。在辅助线程上运行进程时,您永远不希望主线程等待以完成操作。这只是浪费......你可能只是在主线程上运行操作并完成了它。
相反,问题是:如何让主(父)线程处理辅助(子)线程的完成?
根据您收到的错误消息,您似乎正在使用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
}
换句话说,继续按照现在的步骤安排你的任务,但是使用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
}
答案 1 :(得分: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
}