所有,我有一个漫长的运行过程,我使用Task Paralell Library(TPL)在后台线程(具有取消支持)上运行。这个长期运行的代码的代码包含在Class Validation
中,当方法
public bool AsyncRunValidationProcess(TaskScheduler _uiScheduler,
CancellationToken _token, dynamic _dynamic = null)
{
try
{
// Note: _uiScheduler is used to update the UI thread with progress infor etc.
for (int i = 0; i < someLargeLoopNumber; i++)
{
// Cancellation requested from UI Thread.
if (_token.IsCancellationRequested)
_token.ThrowIfCancellationRequested();
}
return true;
}
catch (Exception eX)
{
// Do stuff. Display `eX` if cancellation requested.
return false;
}
}
从Class Validation
运行我可以取消此过程。取消请求由相应的delegate
处理(如下所示),这很好(我不相信这是我的问题的原因)。
当我从另一个类Class Batch
运行此方法时,我通过“控制器”方法执行此操作
asyncTask = Task.Factory.StartNew<bool>(() => asyncControlMethod(), token);
反过来调用方法
valForm.AsyncRunValidationProcess(uiScheduler, token,
new List<string>() { strCurrentSiteRelPath }));
其中valForm
是我Class Validation
的访问者,该方法运行良好,但当我尝试取消delegate
cancelHandler = delegate
{
UtilsTPL.CancelRunningProcess(asyncTask, cancelSource);
};
,其中
public static void CancelRunningProcess(Task _task,
CancellationTokenSource _cancelSource)
{
try
{
_cancelSource.Cancel();
_task.Wait(); // On cross-class call it freezes here.
}
catch (AggregateException aggEx)
{
if (aggEx.InnerException is OperationCanceledException)
Utils.InfoMsg("Operation cancelled at users request.");
if (aggEx.InnerException is SqlException)
Utils.ErrMsg(aggEx.Message);
}
}
在_task.Wait()
上冻结/挂起(没有未处理的异常等)。这(我相信 - 通过测试)与我取消调用asyncControlMethod()
的{{1}}这一事实有关,因此它取消导致当前进程挂起的valForm.AsyncRunValidationProcess(...)
。问题似乎是将asyncControlMethod()
等传递给子方法。 CancellationTokenSource
事件触发并终止控制方法,导致子方法挂起。
任何人都可以告诉我我做错了什么或(更有针对性),我应该做些什么才能允许这样的取消程序?
注意:我尝试生成一个子任务来运行IsCancellationPending
,并使用自己的valForm.AsyncRunValidationProcess(...)
,但这没有用。
感谢您的时间。
答案 0 :(得分:0)
这个问题的答案(由Jiaji Wu的评论和链接大量帮助)是你不能将CancellationToken
声明为传递给级联方法的全局变量;也就是说,你不能拥有
public class MainClass
{
private CancellationTokenSource = source;
private CancellationToken token;
public MainClass()
{
source = new CancellationtokenSource();
token = source.Token;
}
private void buttonProcessSel_Click(object sender, EventArgs e)
{
// Spin-off MyMethod on background thread.
Task<bool> asyncControllerTask = null;
TaskSpin(asyncControllerTask, cancelSource, token, MyMethod);
}
private void method()
{
// Use the global token DOES NOT work!
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
}
private void TaskSpin(Task<bool> asyncTask, CancellationTokenSource cancelSource,
CancellationToken token, Func<bool> asyncMethod)
{
try
{
token = cancelSource.Token;
asyncTask = Task.Factory.StartNew<bool>(() => asyncMethod(token), token);
// To facilitate multitasking the cancelTask ToolStripButton
EventHandler cancelHandler = null;
if (cancelSource != null)
{
cancelHandler = delegate
{
UtilsTPL.CancelRunningProcess(mainForm, uiScheduler, asyncTask, cancelSource, true);
};
}
// Callback for finish/cancellation.
asyncTask.ContinueWith(task =>
{
// Handle cancellation etc.
}
// Other stuff...
}
}
}
在后台线程的maethod运行中使用全局令牌不起作用!该方法必须显式传递token
才能注册它。我不确定为什么会出现这种情况的确切原因,但我将来会知道,现在你需要将令牌传递给MyMethod()
这样
private void method(CancellationToken token)
{
// Use the global token DOES NOT work!
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
}
我希望这有助于其他人。