Dalam utas, saya membuat beberapa System.Threading.Task
dan memulai setiap tugas.
Ketika saya melakukan .Abort()
untuk membunuh utas, tugas tidak dibatalkan.
Bagaimana saya bisa mengirimkan .Abort()
ke tugas saya?
Dalam utas, saya membuat beberapa System.Threading.Task
dan memulai setiap tugas.
Ketika saya melakukan .Abort()
untuk membunuh utas, tugas tidak dibatalkan.
Bagaimana saya bisa mengirimkan .Abort()
ke tugas saya?
Jawaban:
Kamu tidak bisa Tugas menggunakan utas latar belakang dari kumpulan utas. Juga membatalkan utas menggunakan metode Abort tidak dianjurkan. Anda dapat melihat posting blog berikut yang menjelaskan cara yang tepat untuk membatalkan tugas menggunakan token pembatalan. Ini sebuah contoh:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Task.Wait(TimeSpan / int)
untuk memberikan batas waktu (berdasarkan waktu) dari luar.
Task
? Sesuatu seperti: public int StartNewTask(Action method)
. Di dalam StartNewTask
metode yang saya membuat yang baru Task
dengan: Task task = new Task(() => method()); task.Start();
. Jadi bagaimana saya bisa mengelola CancellationToken
? Saya juga ingin tahu jika ingin Thread
menerapkan logika untuk memeriksa apakah ada beberapa Tugas yang masih menggantung dan bunuh mereka kapan Form.Closing
. Dengan Threads
saya gunakan Thread.Abort()
.
Membatalkan tugas dapat dilakukan dengan mudah jika Anda menangkap utas tempat tugas berjalan. Berikut adalah contoh kode untuk menunjukkan ini:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
Saya menggunakan Task.Run () untuk menunjukkan use-case yang paling umum untuk ini - menggunakan kenyamanan Tasks dengan kode single-threaded lama, yang tidak menggunakan kelas PembatalanTokenSource untuk menentukan apakah harus dibatalkan atau tidak.
CancellationToken
dukungan ...
CancellationToken
atau bahkan lebih sederhana yang bebas kondisi ras harus dipertimbangkan. Kode di atas hanya menggambarkan metode, bukan bidang penggunaan.
thread
variabel lokal). Dalam kode Anda, Anda mungkin akhirnya membatalkan utas utama, yang sebenarnya bukan yang Anda inginkan. Mungkin sebuah cek apakah
Seperti yang disarankan pos ini , ini dapat dilakukan dengan cara berikut:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Meskipun berhasil, tidak disarankan untuk menggunakan pendekatan seperti itu. Jika Anda dapat mengontrol kode yang menjalankan tugas, Anda sebaiknya menggunakan penanganan pembatalan yang benar.
Hal semacam ini adalah salah satu alasan logistik mengapa Abort
usang. Pertama dan terutama, jangan gunakan Thread.Abort()
untuk membatalkan atau menghentikan utas jika memungkinkan. Abort()
seharusnya hanya digunakan untuk secara paksa membunuh utas yang tidak menanggapi permintaan yang lebih damai untuk berhenti tepat waktu.
Karena itu, Anda perlu memberikan indikator pembatalan bersama yang satu set dan tunggu sementara thread lainnya secara berkala memeriksa dan keluar dengan anggun. .NET 4 mencakup struktur yang dirancang khusus untuk tujuan ini, the CancellationToken
.
Anda seharusnya tidak mencoba melakukan ini secara langsung. Rancang tugas Anda untuk bekerja dengan Pembatalan , dan batalkan dengan cara ini.
Selain itu, saya akan merekomendasikan untuk mengubah utas utama Anda agar berfungsi melalui CancellingToken juga. Memanggil Thread.Abort()
adalah ide yang buruk - itu dapat menyebabkan berbagai masalah yang sangat sulit untuk didiagnosis. Alih-alih, utas itu dapat menggunakan Pembatalan yang sama dengan yang digunakan tugas Anda - dan yang sama CancellationTokenSource
dapat digunakan untuk memicu pembatalan semua tugas Anda dan utas utama Anda.
Ini akan mengarah pada desain yang jauh lebih sederhana, dan lebih aman.
Untuk menjawab pertanyaan Prerak K tentang cara menggunakan CancurnToken ketika tidak menggunakan metode anonim di Task.Factory.StartNew (), Anda meneruskan CancellingToken sebagai parameter ke dalam metode yang Anda mulai dengan StartNew (), seperti yang ditunjukkan dalam contoh MSDN di sini .
misalnya
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
Saya menggunakan pendekatan campuran untuk membatalkan tugas.
Lihat contoh di bawah ini:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
Tugas memiliki dukungan kelas satu untuk pembatalan melalui token pembatalan . Buat tugas Anda dengan token pembatalan, dan batalkan tugas melalui ini secara eksplisit.
Anda dapat menggunakan a CancellationToken
untuk mengontrol apakah tugas dibatalkan. Apakah Anda berbicara tentang membatalkannya sebelum dimulai ("tidak pernah, saya sudah melakukan ini"), atau benar-benar memotongnya di tengah? Jika yang pertama, CancellationToken
bisa bermanfaat; jika yang terakhir, Anda mungkin perlu menerapkan mekanisme "bail out" Anda sendiri dan memeriksa pada titik yang sesuai dalam pelaksanaan tugas apakah Anda harus gagal dengan cepat (Anda masih dapat menggunakan CancellingToken untuk membantu Anda, tetapi ini sedikit lebih manual).
MSDN memiliki artikel tentang pembatalan Tugas: http://msdn.microsoft.com/en-us/library/dd997396.aspx
Tugas sedang dieksekusi di ThreadPool (setidaknya, jika Anda menggunakan pabrik default), sehingga membatalkan utas tidak dapat memengaruhi tugas. Untuk tugas yang dibatalkan, lihat Pembatalan Tugas di msdn.
Saya mencoba CancellationTokenSource
tetapi saya tidak bisa melakukan ini. Dan saya melakukan ini dengan cara saya sendiri. Dan itu berhasil.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
Dan kelas lain yang memanggil metode:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
Anda dapat membatalkan tugas seperti benang jika Anda dapat menyebabkan tugas yang akan dibuat pada thread sendiri dan memanggil Abort
pada perusahaan Thread
objek. Secara default, tugas dijalankan pada utas kumpulan utas atau utas panggilan - yang tidak ingin Anda batalkan.
Untuk memastikan tugas mendapatkan utasnya sendiri, buat penjadwal khusus yang berasal dari TaskScheduler
. Dalam implementasi Anda QueueTask
, buat utas baru dan gunakan untuk menjalankan tugas. Kemudian, Anda dapat membatalkan utas, yang akan menyebabkan tugas selesai dalam keadaan rusak dengan a ThreadAbortException
.
Gunakan penjadwal tugas ini:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Mulai tugas Anda seperti ini:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Kemudian, Anda dapat membatalkan dengan:
scheduler.TaskThread.Abort();
Perhatikan bahwa peringatan tentang pembatalan utas masih berlaku:
The
Thread.Abort
metode harus digunakan dengan hati-hati. Khususnya ketika Anda menyebutnya untuk membatalkan utas selain utas saat ini, Anda tidak tahu kode apa yang telah dieksekusi atau gagal dijalankan ketika ThreadAbortException dilemparkan, Anda juga tidak bisa memastikan keadaan aplikasi Anda atau aplikasi apa pun dan negara pengguna bahwa itu bertanggung jawab untuk melestarikan. Misalnya, panggilanThread.Abort
dapat mencegah konstruktor statis dari mengeksekusi atau mencegah pelepasan sumber daya yang tidak dikelola.
Thread.Abort
tidak didukung pada .NET Core. Mencoba menggunakannya di sana menghasilkan pengecualian: System.PlatformNotSupportedException: Thread abort tidak didukung pada platform ini. Peringatan ketiga adalah bahwa SingleThreadTaskScheduler
tidak dapat digunakan secara efektif dengan tugas gaya janji, dengan kata lain dengan tugas yang dibuat dengan async
delegasi. Misalnya tertanam await Task.Delay(1000)
berjalan di utas, jadi tidak terpengaruh menjadi acara utas.