1

总之,我有一个长时间运行的进程,我使用任务并行库 (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()已调用的事实有关valForm.AsyncRunValidationProcess(...),因此它正在取消asyncControlMethod()导致当前进程挂起。问题似乎在于将CancellationTokenSource等传递给子方法。该IsCancellationPending事件触发并终止控制方法,从而导致子方法挂起。

谁能告诉我我做错了什么或者(更相关地),我应该怎么做才能允许这样的取消程序?

注意:我试图生成一个子任务来运行valForm.AsyncRunValidationProcess(...)CancellationToken但它没有奏效。

谢谢你的时间。

4

1 回答 1

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...
        }
    }
}

在后台线程上运行的方法中使用全局令牌不起作用!该方法必须显式传递token才能注册它。我不确定为什么会这样的确切原因,但我以后会知道,现在你需要传递令牌来MyMethod()喜欢这个

    private void method(CancellationToken token)
    {
        // Use the global token DOES NOT work!
        if (token.IsCancellationRequested)      
            token.ThrowIfCancellationRequested();
    }

我希望这对其他人有帮助。

于 2012-05-18T10:44:29.123 回答