我有以下代码来构建从SQL Server中提取的高级数据结构,然后当该数据的retrevial完成时,我更新了UI。使用的代码是
private void BuildSelectedTreeViewSectionAsync(TreeNode selectedNode)
{
// Initialise.
SqlServer instance = null;
SqlServer.Database database = null;
// Build and expand the TreeNode.
Task task = null;
task = Task.Factory.StartNew(() => {
string[] tmpStrArr = selectedNode.Text.Split(' ');
string strDatabaseName = tmpStrArr[0];
instance = SqlServer.Instance(this.conn);
database = instance.GetDatabaseFromName(strDatabaseName);
}).ContinueWith(cont => {
instance.BuildTreeViewForSelectedDatabase(this.customTreeViewSql,
selectedNode, database);
selectedNode.Expand();
task.Dispose();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
this.MainUiScheduler);
}
这在我的主要开发机器上应该工作;也就是说,它完成了database
对象的构建,然后在继续更新UI并处理task
(任务对象)。
但是,我一直在另一台计算机上进行一些测试而得到一个InvalidOperationException
,这是因为task.Dispose()
上的task
仍处于Running
状态但是,除非任务已经完成,否则延续cont
永远不会触发。
以下是抛出异常时代码在调试器中的样子:
I am aware that it almost always unneccessary to call Dispose
on tasks。这个问题更多的是关于为何继续在这里解雇?**
答案 0 :(得分:3)
原因很简单,你在继续本身上调用Dispose
而在第一个任务上调用而不是
您的代码包含:
Task task = null;
var task = <task 1>.ContinueWith(t => {
/* task 2 */
task.Dispose();
});
在上面的代码中,task
等于延续(ContinueWith
没有传回原始的Task
,它通过了延续),这就是在闭包中捕获的内容你传递给ContinueWith
。
您可以通过将Task
方法中传递的ContinueWith
参数的引用与task
进行比较来测试:
Task task = null;
var task = <task 1>.ContinueWith(t => {
/* task 2 */
if (object.ReferenceEquals(t, task))
throw new InvalidOperationException("Trying to dispose of myself!");
task.Dispose();
});
为了处理第一个变量,您需要将其分解为两个Task
变量并捕获第一个Task
,如下所示:
var task1 = <task 1>;
var task2 = task1.ContinueWith(t => {
// Dispose of task1 when done.
using (task1)
{
// Do task 2.
}
});
但是,因为previous Task
作为ContinueWith
方法中的参数传递给您,所以您根本不需要在闭包中捕获task
,只需调用Dispose
上的Task
作为参数传递给您:
var task = <task 1>.ContinueWith(t => {
// t = task 1
// task = task 2
// Dispose of task 1 when done.
using (t)
{
// Do task 2.
}
});
答案 1 :(得分:1)
我很确定你在上面尝试做的事情是公平的:
task = Task.Factory.StartNew(() => ...);
task.ContinueWith(cont => { ... task.Dispose(); });
但是,使用您的代码分配给任务变量的内容将是ContinueWith工作项,而不是origninal StartNew工作项。
更重要的是,在这种情况下,您甚至可能不需要担心task.Dispose()。
执行任务时唯一有实际价值的时候.Dispose()是指某个任务涉及的任务.Wait(),它分配了一个操作系统等待处理资源。