多线程跨类取消与TPL [英] Multi-Threading Cross-Class Cancellation with TPL
问题描述
所有的,我有我的后台线程(与取消支持)运行使用任务Paralell库(TPL)一个长期运行的进程。这个长期运行的TAKS的代码包含类验证
中,当方法
All, I have a long running process that I run on a background thread (with cancellation support) using the Task Paralell Library (TPL). The code for this long running taks is contained within Class Validation
, and when the method
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;
}
}
从运行级验证
我可以取消该过程罚款。取消请求是通过适当的委托办理
(如下图所示),并能正常工作(我不相信的,这是我的问题的原因)。
is run from Class Validation
I can cancel the process fine. The cancellation request is handled by the appropriate delegate
(shown below) and this works fine (I don't belive this is the cause of my problem).
当我从另一个类运行此方法,类批次
,我通过控制的方法做到这一点。
When I run this method from another class, Class Batch
, I do this via a "controller" method
asyncTask = Task.Factory.StartNew<bool>(() => asyncControlMethod(), token);
这反过来又调用方法
which in turn invokes the method
valForm.AsyncRunValidationProcess(uiScheduler, token,
new List<string>() { strCurrentSiteRelPath }));
其中, valForm
是我的访问到类验证
,方法运行良好,但是当我尝试注销代表
where valForm
is my accessor to Class Validation
, the method runs fine, but when I attempt a cancellation the delegate
cancelHandler = delegate
{
UtilsTPL.CancelRunningProcess(asyncTask, cancelSource);
};
其中,
where
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()
已叫 valForm.AsyncRunValidationProcess(做... )
,因此它被取消 asyncControlMethod()
这是造成当前进程挂起。这个问题似乎与传递 CancellationTokenSource
等来了孩子的方法。在 IsCancellationPending
事件触发,并杀死控制方法,这会导致孩子的方法挂起。
freezes/hangs (with no unhandled exception etc.) on _task.Wait()
. This (I belive - through testing) is to do with the fact that I am cancelling asyncControlMethod()
which has called valForm.AsyncRunValidationProcess(...)
, so it is cancelling asyncControlMethod()
which is causing the current process to hang. The problem seems to be with passing the CancellationTokenSource
etc. to the child method. The IsCancellationPending
event fires and kills the controlling method, which causes the child method to hang.
CAN谁能告诉我什么,我做错了或者(更贴切),我应该怎么做,让这样的撤销程序
的?注意:我试图生成一个子任务运行 valForm.AsyncRunValidationProcess(...)
,有自己的的CancellationToken
,但这并没有奏效。的
Note: I have tried to spawn a child task to run valForm.AsyncRunValidationProcess(...)
, with its own CancellationToken
but this has not worked.
感谢您的时间。
推荐答案
回答这个问题(由嘉积武的评论和链接大量帮助)是,你不能声明的CancellationToken
作为传递给级联方式的全局变量;也就是说,你不能有
The answer to this problem (helped massively by Jiaji Wu's comment and link) was that you cannot declare the CancellationToken
as a global variable that is passed to the cascading methods; that is, you cannot have
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运行
使用全球令牌在后台线程DOEN不行!该方法必须明确通过标记
为它能够注册。我不知道确切的原因,这是如此,但我知道在未来,现在你需要将令牌传递给的MyMethod()
像这样
Use of the global token in the maethod run on the background thread doen NOT work! The method must be explicitly passed the token
for it to be able to register it. I am not sure of the exact reason why this is the case, but I will know in future, now you need to pass the token to MyMethod()
like this
private void method(CancellationToken token)
{
// Use the global token DOES NOT work!
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
}
我希望这可以帮助别人
这篇关于多线程跨类取消与TPL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!