使用TPL进行多线程跨类取消

时间:2012-05-14 12:22:40

标签: c# multithreading task-parallel-library cancellation

所有,我有一个漫长的运行过程,我使用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(...),但这没有用。

感谢您的时间。

1 个答案:

答案 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();
    }

我希望这有助于其他人。