Programing

대기중인 작업을 취소하는 방법?

lottogame 2020. 6. 16. 21:56
반응형

대기중인 작업을 취소하는 방법?


이 Windows 8 WinRT 작업을하고 있는데 아래 방법을 사용하여 작업을 취소하려고하는데 어느 정도 작동합니다. CancelNotification 메소드 DOES가 호출되어 태스크가 취소되었다고 생각하지만 백그라운드에서 태스크가 계속 실행되고 완료된 후에는 태스크 상태가 항상 완료되고 절대 취소되지 않습니다. 작업이 취소 될 때 작업을 완전히 중단 할 수있는 방법이 있습니까?

private async void TryTask()
{
    CancellationTokenSource source = new CancellationTokenSource();
    source.Token.Register(CancelNotification);
    source.CancelAfter(TimeSpan.FromSeconds(1));
    var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);

    await task;            

    if (task.IsCompleted)
    {
        MessageDialog md = new MessageDialog(task.Result.ToString());
        await md.ShowAsync();
    }
    else
    {
        MessageDialog md = new MessageDialog("Uncompleted");
        await md.ShowAsync();
    }
}

private int slowFunc(int a, int b)
{
    string someString = string.Empty;
    for (int i = 0; i < 200000; i++)
    {
        someString += "a";
    }

    return a + b;
}

private void CancelNotification()
{
}

읽기 최대 취소 (.NET 4.0에 도입 된 이후 큰 변화가되었습니다)와 작업 기반 비동기 패턴 사용하는 방법에 대한 지침을 제공하고, CancellationToken함께 async방법을.

요약하면, CancellationToken취소를 지원하는 각 메소드에 a 전달하면 해당 메소드가 주기적으로 확인해야합니다.

private async Task TryTask()
{
  CancellationTokenSource source = new CancellationTokenSource();
  source.CancelAfter(TimeSpan.FromSeconds(1));
  Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);

  // (A canceled task will raise an exception when awaited).
  await task;
}

private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
  string someString = string.Empty;
  for (int i = 0; i < 200000; i++)
  {
    someString += "a";
    if (i % 1000 == 0)
      cancellationToken.ThrowIfCancellationRequested();
  }

  return a + b;
}

또는 수정을 피하려면 slowFunc(예를 들어 소스 코드에 액세스 할 수 없음) :

var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code

//original code: await task;  
await Task.WhenAny(task, completionSource.Task); //New code

https://github.com/StephenCleary/AsyncEx 에서 멋진 확장 방법을 사용 하여 다음과 같이 간단하게 만들 수 있습니다.

await Task.WhenAny(task, source.Token.AsTask());

다루지 않은 한 가지 경우는 비동기 메서드 내에서 취소를 처리하는 방법입니다. 예를 들어, 서비스에 일부 데이터를 업로드하여 무언가를 계산 한 다음 결과를 반환해야하는 간단한 경우를 생각해보십시오.

public async Task<Results> ProcessDataAsync(MyData data)
{
    var client = await GetClientAsync();
    await client.UploadDataAsync(data);
    await client.CalculateAsync();
    return await client.GetResultsAsync();
}

If you want to support cancellation then the easiest way would be to pass in a token and check if it has been cancelled between each async method call (or using ContinueWith). If they are very long running calls though you could be waiting a while to cancel. I created a little helper method to instead fail as soon as canceled.

public static class TaskExtensions
{
    public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
    {
        token.ThrowIfCancellationRequested();
        await Task.WhenAny(task, token.WhenCanceled());
        token.ThrowIfCancellationRequested();

        return await task;
    }

    public static Task WhenCanceled(this CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<bool>();
        cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
        return tcs.Task;
    }
}

So to use it then just add .WaitOrCancel(token) to any async call:

public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
    Client client;
    try
    {
        client = await GetClientAsync().WaitOrCancel(token);
        await client.UploadDataAsync(data).WaitOrCancel(token);
        await client.CalculateAsync().WaitOrCancel(token);
        return await client.GetResultsAsync().WaitOrCancel(token);
    }
    catch (OperationCanceledException)
    {
        if (client != null)
            await client.CancelAsync();
        throw;
    }
}

Note that this will not stop the Task you were waiting for and it will continue running. You'll need to use a different mechanism to stop it, such as the CancelAsync call in the example, or better yet pass in the same CancellationToken to the Task so that it can handle the cancellation eventually. Trying to abort the thread isn't recommended.


I just want to add to the already accepted answer. I was stuck on this, but I was going a different route on handling the complete event. Rather than running await, I add a completed handler to the task.

Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);

Where the event handler looks like this

private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
    if (status == AsyncStatus.Canceled)
    {
        return;
    }
    CommentsItemsControl.ItemsSource = Comments.Result;
    CommentScrollViewer.ScrollToVerticalOffset(0);
    CommentScrollViewer.Visibility = Visibility.Visible;
    CommentProgressRing.Visibility = Visibility.Collapsed;
}

With this route, all the handling is already done for you, when the task is cancelled it just triggers the event handler and you can see if it was cancelled there.

참고URL : https://stackoverflow.com/questions/10134310/how-to-cancel-a-task-in-await

반응형