Panduan: Mendengarkan Permintaan Pembatalan dengan Polling
Contoh berikut menunjukkan satu cara agar kode pengguna dapat melakukan polling token pembatalan secara berkala untuk melihat apakah pembatalan telah diminta dari utas panggilan. Contoh ini menggunakan jenis System.Threading.Tasks.Task, tetapi pola yang sama berlaku untuk operasi asinkron yang dibuat langsung oleh jenis System.Threading.ThreadPool atau jenis System.Threading.Thread.
Contoh
Polling memerlukan semacam perulangan atau kode rekursif yang dapat membaca nilai properti Boolean IsCancellationRequested secara periodik. Jika Anda menggunakan jenis System.Threading.Tasks.Task dan menunggu tugas selesai pada utas panggilan, Anda dapat menggunakan metode ThrowIfCancellationRequested untuk memeriksa properti dan melemparkan pengecualian. Dengan menggunakan metode ini, Anda memastikan bahwa pengecualian yang benar dilemparkan sebagai tanggapan atas permintaan. Jika Anda menggunakan Task, maka memanggil metode ini lebih baik daripada melemparkan OperationCanceledException secara manual. Jika Anda tidak perlu melemparkan pengecualian, maka Anda hanya dapat memeriksa properti dan kembali dari metode jika properti adalah true
.
using System;
using System.Threading;
using System.Threading.Tasks;
public struct Rectangle
{
public int columns;
public int rows;
}
class CancelByPolling
{
static void Main()
{
var tokenSource = new CancellationTokenSource();
// Toy object for demo purposes
Rectangle rect = new Rectangle() { columns = 1000, rows = 500 };
// Simple cancellation scenario #1. Calling thread does not wait
// on the task to complete, and the user delegate simply returns
// on cancellation request without throwing.
Task.Run(() => NestedLoops(rect, tokenSource.Token), tokenSource.Token);
// Simple cancellation scenario #2. Calling thread does not wait
// on the task to complete, and the user delegate throws
// OperationCanceledException to shut down task and transition its state.
// Task.Run(() => PollByTimeSpan(tokenSource.Token), tokenSource.Token);
Console.WriteLine("Press 'c' to cancel");
if (Console.ReadKey(true).KeyChar == 'c') {
tokenSource.Cancel();
Console.WriteLine("Press any key to exit.");
}
Console.ReadKey();
tokenSource.Dispose();
}
static void NestedLoops(Rectangle rect, CancellationToken token)
{
for (int col = 0; col < rect.columns && !token.IsCancellationRequested; col++) {
// Assume that we know that the inner loop is very fast.
// Therefore, polling once per column in the outer loop condition
// is sufficient.
for (int row = 0; row < rect.rows; row++) {
// Simulating work.
Thread.SpinWait(5_000);
Console.Write("{0},{1} ", col, row);
}
}
if (token.IsCancellationRequested) {
// Cleanup or undo here if necessary...
Console.WriteLine("\r\nOperation canceled");
Console.WriteLine("Press any key to exit.");
// If using Task:
// token.ThrowIfCancellationRequested();
}
}
}
Imports System.Threading
Imports System.Threading.Tasks
Public Structure Rectangle
Public columns As Integer
Public rows As Integer
End Structure
Class CancelByPolling
Shared Sub Main()
Dim tokenSource As New CancellationTokenSource()
' Toy object for demo purposes
Dim rect As New Rectangle()
rect.columns = 1000
rect.rows = 500
' Simple cancellation scenario #1. Calling thread does not wait
' on the task to complete, and the user delegate simply returns
' on cancellation request without throwing.
Task.Run(Sub() NestedLoops(rect, tokenSource.Token), tokenSource.Token)
' Simple cancellation scenario #2. Calling thread does not wait
' on the task to complete, and the user delegate throws
' OperationCanceledException to shut down task and transition its state.
' Task.Run(Sub() PollByTimeSpan(tokenSource.Token), tokenSource.Token)
Console.WriteLine("Press 'c' to cancel")
If Console.ReadKey(True).KeyChar = "c"c Then
tokenSource.Cancel()
Console.WriteLine("Press any key to exit.")
End If
Console.ReadKey()
tokenSource.Dispose()
End Sub
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
Dim col As Integer
For col = 0 To rect.columns - 1
' Assume that we know that the inner loop is very fast.
' Therefore, polling once per column in the outer loop condition
' is sufficient.
For col As Integer = 0 To rect.rows - 1
' Simulating work.
Thread.SpinWait(5000)
Console.Write("0',1' ", x, y)
Next
Next
If token.IsCancellationRequested = True Then
' Cleanup or undo here if necessary...
Console.WriteLine(vbCrLf + "Operation canceled")
Console.WriteLine("Press any key to exit.")
' If using Task:
' token.ThrowIfCancellationRequested()
End If
End Sub
End Class
Panggilan ThrowIfCancellationRequested sangat cepat dan tidak memperkenalkan overhead yang signifikan dalam perulangan.
Jika Anda memanggil ThrowIfCancellationRequested, Anda hanya perlu secara eksplisit memeriksa properti IsCancellationRequested jika Anda memiliki pekerjaan lain yang harus dilakukan sebagai respons terhadap pembatalan selain melemparkan pengecualian. Dalam contoh ini, Anda dapat melihat bahwa kode benar-benar mengakses properti dua kali: sekali dalam akses eksplisit dan sekali lagi dalam metode ThrowIfCancellationRequested. Tetapi karena tindakan membaca properti IsCancellationRequested hanya melibatkan satu instruksi baca volatil per akses, akses ganda tidak signifikan dari perspektif performa. Masih lebih baik memanggil metode daripada melemparkan OperationCanceledException secara manual.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk