Bagaimana untuk membatalkan Tugas dalam menunggu?

I'm bermain dengan Windows 8 WinRT tugas, dan I'm mencoba untuk membatalkan tugas menggunakan metode di bawah ini, dan ia bekerja untuk beberapa titik. Yang CancelNotification metode TIDAK bisa disebut, yang membuat anda berpikir tugas itu dibatalkan, tetapi di latar belakang tugas terus berjalan, kemudian setelah itu's selesai, status Tugas selalu selesai dan tidak pernah dibatalkan. Apakah ada cara untuk benar-benar menghentikan tugas yang ketika itu's dibatalkan?

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()
{
}
Mengomentari pertanyaan (1)
Larutan

Baca di Pembatalan (yang diperkenalkan pada .NET 4.0 dan sebagian besar tidak berubah sejak itulah) dan Tugas Berbasis Asynchronous Pola, yang menyediakan panduan tentang bagaimana untuk menggunakan CancellationToken dengan async metode.

Untuk meringkas, anda melewati CancellationToken ke masing-masing metode yang mendukung pembatalan, dan bahwa metode harus memeriksa secara berkala.

private async Task TryTask()
{
  CancellationTokenSource source = new CancellationTokenSource();
  source.CancelAfter(TimeSpan.FromSeconds(1));
  Task 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;
}
Komentar (22)

Atau, dalam rangka untuk menghindari memodifikasi slowFunc (mengatakan anda don't memiliki akses ke kode sumber untuk contoh):

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

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

Anda juga dapat menggunakan baik metode ekstensi dari https://github.com/StephenCleary/AsyncEx dan itu terlihat sederhana seperti:

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

Salah satu kasus yang belum't telah tertutup adalah bagaimana untuk menangani pembatalan dalam sebuah metode async. Ambil contoh kasus sederhana di mana anda perlu untuk meng-upload beberapa data untuk mendapatkan layanan ini untuk menghitung sesuatu dan kemudian kembali beberapa hasil.

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

Jika anda ingin mendukung pembatalan maka cara termudah adalah untuk lulus dalam token dan memeriksa apakah itu telah dibatalkan antara masing-masing metode async panggilan (atau menggunakan ContinueWith). Jika mereka sangat lama menjalankan panggilan meskipun anda bisa menunggu beberapa saat untuk membatalkan. Saya dibuat sedikit helper metode untuk bukan gagal segera dibatalkan.

public static class TaskExtensions
{
    public static async Task WaitOrCancel(this Task 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();
        cancellationToken.Register(s => ((TaskCompletionSource)s).SetResult(true), tcs);
        return tcs.Task;
    }
}

Jadi untuk menggunakannya maka tambahkan saja .WaitOrCancel(token) untuk setiap async hubungi:

public async Task 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;
    }
}

Catatan bahwa ini tidak akan menghentikan Tugas anda sedang menunggu dan akan terus berjalan. Anda'll harus menggunakan mekanisme yang berbeda untuk menghentikannya, seperti CancelAsync panggilan di contoh, atau lebih baik lagi lulus dalam sama CancellationToken untuk Tugas sehingga dapat menangani pembatalan akhirnya. Mencoba untuk membatalkan thread isn't direkomendasikan.

Komentar (4)

Saya hanya ingin menambahkan untuk yang sudah diterima menjawab. Aku terjebak pada hal ini, tapi aku akan pergi rute yang berbeda pada penanganan peristiwa yang lengkap. Daripada berjalan menunggu, saya menambahkan melengkapi verifikasi identitas handler untuk tugas.

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

Dimana event handler terlihat seperti ini

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

Dengan rute ini, semua penanganan sudah dilakukan untuk anda, ketika tugas yang dibatalkan itu hanya memicu event handler dan anda dapat melihat apakah itu dibatalkan di sana.

Komentar (0)