Правильный способ отмены выполнения метода

Возможный дубликат:
Как мне прервать/ отменить задачи TPL?

У меня есть метод, выполнение которого занимает некоторое время, поэтому я возвращаю результат как обратный вызов. Мой метод выглядит так:

public static void DoWork( Action<object> onCompleteCallBack)
{
  Task.Factory.StartNew( () => {
    // Do work
    onCompleteCallBack(someResult);
  });
}

Теперь я хотел бы иметь возможность остановить выполнение этого метода, если пользователь не хочет ждать. В результате вот что у меня получилось:

static void Main ( string[] args )
{            
  var cancelMethod = DoWork( x =>
  {                
    // method completed
    Console.Write( x.ToString() );
  });
  Thread.Sleep( 5000 ); // some time passes 

  // then user decides to abort method
  cancelMethod();
  Console.Read();
}

static Action DoWork ( Action<object> onCompleteCallBack )
{
  bool stopExecuting = false;
  Task.Factory.StartNew( () =>
  {
    for ( var i = 0 ; i < 100000 ; i++ ) 
    {
      Thread.Sleep( 1 );
      if ( stopExecuting )
      {
        onCompleteCallBack( "Method aborted!" );
        return;
      }
    }         
    onCompleteCallBack( "Method completed successfully" );
  } );
  return () => { stopExecuting = true; };
}

Какой будет более подходящий способ прервать выполнение метода?

Редактировать

Спасибо за ваши ответы. Теперь я вспомнил о жетоне отмены. Токен трудно запомнить. Я думаю, что я буду использовать этот подход:

    static void Main ( string[ ] args )
    {
        Action abortTask;

        DoWork( methodCompleted, out abortTask );

        Thread.Sleep( 5000 ); // some time passes then user decides to abort method

        // cancel execution of method
        abortTask( );

        Console.Read( );
    }

    static void methodCompleted ( object msg )
    {            
        Console.Write( msg.ToString( ) );  
    }

    static void DoWork ( Action<object> onCompleteCallBack, out Action abortThisTask )
    {
        bool stopExecuting = false;

        abortThisTask = ( ) => { stopExecuting = true; };

        Task.Factory.StartNew( ( ) =>
        {
            for ( var i = 0 ; i < 100000 ; i++ ) 
            {
                Thread.Sleep( 1 );

                if ( stopExecuting )
                {
                    onCompleteCallBack( "Method aborted!" );
                    return;
                }
            }

            onCompleteCallBack( "Method completed successfully" );
        } );            
    }

    // Overloaded method
    static void DoWork ( Action<object> onCompleteCallBack )
    {
        Action abortTask;
        DoWork( onCompleteCallBack ,out abortTask );
    }

Будет ли лучше использовать подходы, которые вы, ребята, предложили в ответах на этот вопрос, по сравнению с этим подходом. Такой подход мне нравится больше. Я думаю, что его легче читать, чем токен отмены.

PS. В моей Visual Studio много места. Не стесняйтесь форматировать код :)


person Tono Nam    schedule 20.11.2012    source источник
comment
спасибо за ссылку, я постараюсь закрыть свой ответ, это то, что я искал.   -  person Tono Nam    schedule 21.11.2012


Ответы (5)


попробуйте использовать токен отмены, указав свою функцию в задаче http://blogs.msdn.com/b/csharpfaq/archive/2010/07/19/parallel-programming-task-cancellation.aspx

CancellationTokenSource tokenSource = new CancellationTokenSource();
 Task.Factory.StartNew( () => {
    // Do work
    onCompleteCallBack(someResult);
  }, tokenSource.Token);

private void cancel_Click(object sender, RoutedEventArgs e)
{
    tokenSource.Cancel();

}
person COLD TOLD    schedule 20.11.2012

Посмотрите на CancellationToken и руководство Альбахари по безопасному закрытию цепочки.

person pstrjds    schedule 20.11.2012

Существует перегрузка метода StartNew(), которая принимает CancellationToken. Вот пример: http://msdn.microsoft.com/en-us/library/dd997396.aspx. Основы заключаются в том, что вы создаете, а затем передаете этот токен своему методу (или ссылаетесь на него из родительского потока; он потокобезопасен). Затем ваш параллельный метод должен в удобное время (например, в цикле) проверять, указывает ли токен на отмену задачи. Если это так, он изящно выходит.

person KeithS    schedule 20.11.2012

Задания позволяют пройти CancellationToken. Вы можете вызвать ct.Cancel(), чтобы установить флаг отмены. В вашей фактической логике выполнения вы можете добавить проверки в определенные «контрольные точки», которые проверяют if (ct.IsCancellationRequested) и возвращают оттуда метод или, альтернативно, вызывают ct.ThrowIfCancellationRequested().

person Lunyx    schedule 20.11.2012
comment
Какой смысл в ThrowIfCancellationRequested? Я знаю, что этот подход исходит из MSDN, но почему бы просто не использовать Throw New UserCancellationException или что-то другое? - person Neolisk; 21.11.2012
comment
Я не совсем уверен, так как сам в этом новичок; Я просто делюсь тем, что узнал. Лично я просто возвращаю что-то другое вместо исключения. Преимущество создания исключения в целом заключается в наличии логики очистки в обработчике исключений. - person Lunyx; 21.11.2012
comment
Я тоже учусь, но никогда не слышал о ThrowIfCancellationRequested. Если вы объясните преимущества, которые он может предоставить, особенно в отношении контекста, описанного в MSDN, я бы лично дал вам +1 за этот ответ. :) - person Neolisk; 21.11.2012
comment
Этот ответ интересен. Похоже на ThrowIfCancellationRequested = if (token.IsCancellationRequested) throw new OperationCanceledException(token);, хотя последнее гораздо более читабельно, т.е. вы ясно видите тип выбрасываемого исключения. - person Neolisk; 21.11.2012
comment
Мне кажется, что разница между OperationCanceledException и UserCancellationException заключается в том, из каких классов они получены, а также в их использовании. Согласно документации MSDN, UserCancellationException возникает, когда пользователь отменяет операцию во время вызова GetToken(CardSpacePolicyElement[], SecurityTokenSerializer). Этот метод находится в пространстве имен System.IdentityModel.Selectors, тогда как OperationCanceledException происходит от System.Exception, а также содержит исходный CancellationToken, который был отменен. - person Lunyx; 21.11.2012
comment
То, что вы только что сказали, звучит как магия вуду, но спасибо, что изучили это для меня. +1. - person Neolisk; 21.11.2012

http://msdn.microsoft.com/en-us/library/dd537607(v=vs.100).aspx, вероятно, будет хорошим местом для начала. В зависимости от того, для чего вы собираетесь.

person Michael Dunlap    schedule 20.11.2012
comment
OP не использует потоки напрямую. - person Neolisk; 21.11.2012
comment
Нет. Thread.Abort() — это зло. interact-sw.co.uk/iangblog/2004/11/ 12/отмена - person KeithS; 21.11.2012
comment
Да, я вставил URL-адрес не той статьи MSDN. Я такой глупый. - person Michael Dunlap; 21.11.2012