我一直在使用TPL在非UI线程中运行数据库提取,允许UI在它们发生时继续运行。调用以下示例中的代码以在主详细信息视图中填充详细信息窗格。主窗格中有一个树视图,根据单击的节点提取不同的数据。 UI允许用户取消提取,并在提取处于活动状态时选择其他节点时自动取消提取。这是我用来执行此操作的代码:
Protected Overrides Sub FetchSummary()
If DBKey.PresentAndSet(DataKey) Then
_view.BeginDataFetch()
' Cancel any active refresh
If TaskCancelTokenSource IsNot Nothing Then TaskCancelTokenSource.Cancel()
TaskCancelTokenSource = New CancellationTokenSource
Dim ctok = TaskCancelTokenSource.Token
Dim dataTask = New Task(Of IEnumerable(Of IAssignSailingPart.ISummary))(Function() FetchsummaryData(Context, DataKey), ctok)
Dim uiSyncContext = TaskScheduler.FromCurrentSynchronizationContext
dataTask.ContinueWith(Sub(dt) _view.Data = dt.Result, ctok, TaskContinuationOptions.OnlyOnRanToCompletion, uiSyncContext)
dataTask.ContinueWith(Sub(dt) _view.FailDataFetch("There was an error fetching the data, try refreshing"), Nothing, TaskContinuationOptions.OnlyOnFaulted, uiSyncContext)
dataTask.ContinueWith(Sub(dt) _view.Data = New List(Of IAssignSailingPart.ISummary), Nothing, TaskContinuationOptions.OnlyOnCanceled, uiSyncContext)
dataTask.Start()
End If
End Sub
因此,为了启动任务,我们调用一个函数来查询数据库以获得结果。成功之后我们将其发送到视图,在取消时我们将一个空数据集发送到视图,并且在失败时我们告诉用户尝试刷新。
这个似乎可以正常工作。用户对响应性等感到满意。我们最近遇到了一些问题,尽管数据库服务器遇到了一些无关的问题。如果在应用程序的编译版本上获取失败(而不是在IDE中),则在实际失败发生后不久,它将使用未捕获的聚合异常终止应用程序。我已经对此进行了一些研究并理解(或者认为我这样做)异常在任务被垃圾收集时抛出在不同的线程上。
我的问题是我应该如何调整代码来正确处理这个问题?这适用于使用.Net 4.0的Windows窗体应用程序。
答案 0 :(得分:1)
您遇到的问题与未在故障任务中观察异常有关(在本例中为dt
)。每个Task
对象都带有一个标志,指示是否观察/访问了它的异常 - 如果有的话。当Task
对象最终完成并且该标志指示未处理异常时,应用程序将被.NET 4.0关闭。顺便说一句,这是在.NET 4.5中已经改变的行为。 Stephen Toub详细解释了here。
正确处理此问题的方法是在访问dt.Exception
之前查看dt.Result
。当您访问dt.Exception
属性时(为了决定下一步做什么,或者只是为了记录异常),这将导致Task
异常被标记为已观察,并且应用程序将不再在Task
实例的最终确定时崩溃。另一方面,如果dt.Result
出现故障,直接访问Task
只会传播(重新抛出)异常。
我也会进行一次ContinueWith()
调用,并在那里检查Task
状态(原谅我的VB,我是C#dev):
Protected Overrides Sub FetchSummary()
If DBKey.PresentAndSet(DataKey) Then
_view.BeginDataFetch()
' Cancel any active refresh
If TaskCancelTokenSource IsNot Nothing Then TaskCancelTokenSource.Cancel()
TaskCancelTokenSource = New CancellationTokenSource
Dim ctok = TaskCancelTokenSource.Token
Dim dataTask = New Task(Of IEnumerable(Of IAssignSailingPart.ISummary))(Function() FetchsummaryData(Context, DataKey), ctok)
Dim uiSyncContext = TaskScheduler.FromCurrentSynchronizationContext
dataTask.ContinueWith(Sub(dt)
If dt.IsFaulted Then
_view.FailDataFetch("There was an error fetching the data, try refreshing")
Exit Sub
End If
If dt.IsCancelled Then
_view.Data = New List(Of IAssignSailingPart.ISummary)
Exit Sub
End If
_view.Data = dt.Result
End Sub, ctok, uiSyncContext)
dataTask.Start()
End If
End Sub
原因是,当您使用Task
标记TaskContinuationOptions.OnlyOnRanToCompletion
时,如果Task
未运行到Cancelled
,那么该延续dataTask
将被标记为function verifyWorkout(workout, wtype){
var errorlist = "";
try{
for(i=1;i<workout.length;i++){
var cardiocount = 0;
var strengthcount = 0;
var corecount = 0;
var hiitcount = 0;
for(j=1;j<workout[i].length;j++){
for(k=0;k<workout[i][j].length;k++){
if (workout[i][j][k].extype == "Cardio"){
cardiocount += cardiocount + 1;
}else if (workout[i][j][k].extype == "Strength"){
strengthcount += strengthcount + 1;
}else if (workout[i][j][k].extype == "Core"){
corecount += corecount + 1;
}else if (workout[i][j][k].extype == "Hiit"){
hiitcount += hiitcount + 1;
}
}
}
if (wtype === "Beginner"){
if (i = 1){
if (cardiocount < 1){
errorlist = errorlist + "Week 1 requires at least 1 cardio exercise.</br>";
}
if (strengthcount < 1){
errorlist = errorlist + "Week 1 requires at least 1 strength exercise.</br>";
}
if (corecount < 1){
errorlist = errorlist + "Week " + i + " requires at least 1 core exercise.</br>";
}
} else if (i = 2){
if (cardiocount < 2){
errorlist = errorlist + "Week 2 requires at least 2 cardio exercises.</br>";
}
if (strengthcount < 1){
errorlist = errorlist + "Week 2 requires at least 1 strength exercise.</br>";
}
if (corecount < 1){
errorlist = errorlist + "Week " + i + " requires at least 1 core exercise.</br>";
}
} else {
if (cardiocount < 3){
errorlist = errorlist + "Week " + i + " requires at least 3 cardio exercises.</br>";
}
if (strengthcount < 2){
errorlist = errorlist + "Week " + i + " requires at least 2 strength exercises.</br>";
}
if (corecount < 1){
errorlist = errorlist + "Week " + i + " requires at least 1 core exercise.</br>";
}
}
}else if (wtype === "Intermediate"){
if (cardiocount < 3){
errorlist = errorlist + "Week " + i + " requires at least 3 cardio exercises.</br>";
}
if (strengthcount < 2){
errorlist = errorlist + "Week " + i + " requires at least 2 strength exercises.</br>";
}
if (corecount < 1){
errorlist = errorlist + "Week " + i + " requires at least 1 core exercise.</br>";
}
}else if (wtype === "Advanced"){
if (cardiocount < 2){
errorlist = errorlist + "Week " + i + " requires at least 2 cardio exercises.</br>";
}
if (strengthcount < 2){
errorlist = errorlist + "Week " + i + " requires at least 2 strength exercises.</br>";
}
if (hiitcount < 2){
errorlist = errorlist + "Week " + i + " requires at least 2 hiit exercises.</br>";
}
if (corecount < 1){
errorlist = errorlist + "Week " + i + " requires at least 1 core exercise.</br>";
}
}
}
return errorlist;
} catch(err) {
console.log("error retrieving validation....");
console.log(errorlist);
console.log(err);
}
}
完成,只会使问题复杂化。