2012-05-14 5 views
1

Все, у меня есть длительный процесс, который я запускаю в фоновом потоке (с поддержкой отмены), используя панель задач Paralell Library (TPL). Код для этого долго работает TAKS содержится в Class Validation, и когда методMulti-Threading Cross-Class Cancellation with TPL

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(), что приводит к зависанию текущего процесса. Проблема заключается в передаче методу child CancellationTokenSource и т. Д. Событие IsCancellationPending срабатывает и уничтожает метод управления, который заставляет дочерний метод зависать.

Может ли кто-нибудь сказать мне, что я делаю неправильно или (более уместно), что я должен делать, чтобы разрешить такую ​​процедуру отмены?

Примечание: Я пытался породить ребенка задачу для запуска valForm.AsyncRunValidationProcess(...), со своим собственным CancellationToken, но это не сработало.

Спасибо за ваше время.

+0

Как вы начинаете «Задачу»? Почему ваш метод принимает «TaskScheduler», когда он, похоже, не использует его? – svick

+0

Он его использует. Прошедший «TaskScheduler» предназначен для потока пользовательского интерфейса. Я просто не показывал, что он используется, поскольку есть много кода.Он используется для обновления 'DataGridView' и общих элементов управления графическим интерфейсом (индикатор выполнения и т. Д.). – MoonKnight

+0

И как вы начинаете «Задачу»? – svick

ответ

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 перспективе на фоновом поток DOEN НЕ работают! Этот метод должен быть явно передан token, чтобы он мог его зарегистрировать. Я не уверен в точной причины, почему это так, но я буду знать в будущем, теперь вам нужно передать маркер MyMethod() как этот

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

Я надеюсь, что это помогает кому-то еще.

+0

Я чувствую себя немного смущенным, поскольку ответ не кажется таким «relavant». Ваш комментарий к коду 'Использовать глобальный токен НЕ РАБОТАЕТ!' означает, что если вы используете глобальный токен, ваше приложение зависает в строке '_task.Wait()'? Я снова прочитал код в исходном вопросе, и вот что-то я не совсем понимаю: в методе 'CancelRunningProcess', почему вы называете' _cancelSource.Cancel(); '? Поскольку «CancelRunningProcess» - это функция обратного вызова, которая вызывается, если задачи отменены, ее следует вызывать ** после ** вызова '_cancelSource.Cancel();' (где-то еще в вашем коде). –

+0

Я извиняюсь, вопрос несколько плохо поставлен из-за того, что я изначально плохо реагировал и редактировал вопрос «на лету», как я узнал больше. Мне не нужно '_task.Wait()' и использовать его для отладки по-настоящему - игнорируйте это сейчас. Да, я использовал глобальное 'CancellationToken', и это _this_ недействительно. Как только я убедился, что токен передан в различные методы, все они могут быть правильно отменены с использованием одного токена и релевантных проверок. Надеюсь, это прояснит это? – MoonKnight

+0

Примечание. В одном классе я использовал глобальный токен. Затем я передал это в общедоступный метод из другого класса в качестве аргумента - вот почему это вызвало проблему. Спасибо за ваше время. – MoonKnight