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:
classProgram{staticvoidMain(){var ts =newCancellationTokenSource();CancellationToken ct = ts.Token;Task.Factory.StartNew(()=>{while(true){// do some heavy work hereThread.Sleep(100);if(ct.IsCancellationRequested){// another thread decided to cancelConsole.WriteLine("task canceled");break;}}}, ct);// Simulate waiting 3s for the task to completeThread.Sleep(3000);// Can't wait anymore => cancel this task
ts.Cancel();Console.ReadLine();}}
Penjelasan yang bagus. Saya punya pertanyaan, bagaimana cara kerjanya ketika kita tidak memiliki metode anonim di Task.Factory.StartNew? seperti Task.Factory.StartNew (() => ProcessMyMethod (), cancellationToken)
Prerak K
61
bagaimana jika ada panggilan pemblokiran yang tidak kembali dalam tugas eksekusi?
mehmet6parmak
3
@ mehmet6parmak Saya pikir satu-satunya hal yang dapat Anda lakukan adalah menggunakannya Task.Wait(TimeSpan / int)untuk memberikan batas waktu (berdasarkan waktu) dari luar.
Tandai
2
Bagaimana jika saya memiliki kelas khusus saya untuk mengelola pelaksanaan metode di dalam yang baru Task? Sesuatu seperti: public int StartNewTask(Action method). Di dalam StartNewTaskmetode yang saya membuat yang baru Taskdengan: Task task = new Task(() => method()); task.Start();. Jadi bagaimana saya bisa mengelola CancellationToken? Saya juga ingin tahu jika ingin Threadmenerapkan logika untuk memeriksa apakah ada beberapa Tugas yang masih menggantung dan bunuh mereka kapan Form.Closing. Dengan Threadssaya gunakan Thread.Abort().
Cheshire Cat
Omg, contoh yang buruk! Ini adalah kondisi boolean yang sederhana, tentu saja itu yang pertama dicoba! Tetapi saya memiliki fungsi dalam Tugas terpisah, yang mungkin membutuhkan banyak waktu untuk diakhiri, dan idealnya tidak perlu tahu apa-apa tentang threading atau apa pun. Jadi bagaimana cara saya membatalkan fungsi dengan saran Anda?
Hi-Angel
32
Membatalkan tugas dapat dilakukan dengan mudah jika Anda menangkap utas tempat tugas berjalan. Berikut adalah contoh kode untuk menunjukkan ini:
voidMain(){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 displayedConsole.WriteLine("Task finished!");});//This is needed in the example to avoid thread being still NULLThread.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.
Terima kasih atas ide ini. Menggunakan pendekatan ini untuk menerapkan batas waktu untuk beberapa kode eksternal, yang tidak memiliki CancellationTokendukungan ...
Christoph Fink
7
AFAIK thread.abort akan membuat Anda tidak mengetahui tentang tumpukan Anda, itu mungkin tidak valid. Saya belum pernah mencobanya tetapi saya kira memulai utas di domain aplikasi terpisah agar utas.abort akan disimpan! Selain itu, seluruh utas terbuang dalam solusi Anda hanya untuk membatalkan satu tugas. Anda tidak harus menggunakan tugas tetapi utas di tempat pertama. (downvote)
Martin Meeser
1
Seperti yang saya tulis - solusi ini adalah pilihan terakhir yang dapat dipertimbangkan dalam keadaan tertentu. Tentu saja solusi CancellationTokenatau bahkan lebih sederhana yang bebas kondisi ras harus dipertimbangkan. Kode di atas hanya menggambarkan metode, bukan bidang penggunaan.
Florian Rappl
8
Saya pikir pendekatan ini mungkin memiliki konsekuensi yang tidak diketahui dan saya tidak akan merekomendasikannya dalam kode produksi. Tugas tidak selalu dijamin untuk dieksekusi di utas yang berbeda, artinya tugas tersebut dapat dijalankan pada utas yang sama dengan yang dibuat jika penjadwal memutuskan demikian (yang berarti utas utama akan diteruskan ke threadvariabel lokal). Dalam kode Anda, Anda mungkin akhirnya membatalkan utas utama, yang sebenarnya bukan yang Anda inginkan. Mungkin sebuah cek apakah
utasnya
10
@ Martin - Ketika saya telah meneliti pertanyaan ini pada SO, saya melihat Anda telah memilih beberapa jawaban yang menggunakan Thread.Abort untuk membunuh tugas, tetapi Anda belum memberikan solusi alternatif apa pun. Bagaimana Anda bisa membunuh kode pihak ke-3 yang tidak mendukung pembatalan dan sedang menjalankan tugas ?
Gordon Bean
32
Seperti yang disarankan pos ini , ini dapat dilakukan dengan cara berikut:
intFoo(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.
+1 untuk memberikan pendekatan yang berbeda sambil menyatakan fallbacknya. Saya tidak tahu ini bisa dilakukan :)
Joel
1
Terima kasih atas solusinya! Kami hanya bisa meneruskan token ke metode dan membatalkan tokenource, alih-alih mendapatkan instance thread dari metode dan membatalkan instance ini secara langsung.
Hal semacam ini adalah salah satu alasan logistik mengapa Abortusang. 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 CancellationTokenSourcedapat 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 =newCancellationTokenSource();var token = tokenSource.Token;Task.Factory.StartNew(()=>DoSomeWork(1, token), token);staticvoidDoSomeWork(int taskNum,CancellationToken ct){// Do work here, checking and acting on ct.IsCancellationRequested where applicable, }
Saya menggunakan pendekatan campuran untuk membatalkan tugas.
Pertama, saya mencoba untuk Membatalkannya dengan sopan menggunakan Pembatalan .
Jika masih berjalan (mis. Karena kesalahan pengembang), maka berperilaku salah dan membunuhnya menggunakan metode Abort sekolah lama .
Lihat contoh di bawah ini:
privateCancellationTokenSource taskToken;privateAutoResetEvent awaitReplyOnRequestEvent =newAutoResetEvent(false);voidMain(){// Start a task which is doing nothing but sleeps 1sLaunchTaskAsync();Thread.Sleep(100);// Stop the taskStopTask();}/// <summary>/// Launch task in a new thread/// </summary>voidLaunchTaskAsync(){
taskToken =newCancellationTokenSource();Task.Factory.StartNew(()=>{try{//Capture the thread
runningTaskThread =Thread.CurrentThread;// Run the taskif(taskToken.IsCancellationRequested||!awaitReplyOnRequestEvent.WaitOne(10000))return;Console.WriteLine("Task finished!");}catch(Exception exc){// Handle exception}}, taskToken.Token);}/// <summary>/// Stop running task/// </summary>voidStopTask(){// Attempt to cancel the task politelyif(taskToken !=null){if(taskToken.IsCancellationRequested)return;else
taskToken.Cancel();}// Notify a waiting thread that an event has occurredif(awaitReplyOnRequestEvent !=null)
awaitReplyOnRequestEvent.Set();// If 1 sec later the task is still running, kill it cruellyif(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 CancellationTokenuntuk 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, CancellationTokenbisa 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).
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 CancellationTokenSourcetetapi saya tidak bisa melakukan ini. Dan saya melakukan ini dengan cara saya sendiri. Dan itu berhasil.
namespace Blokick.Provider{publicclassSignalRConnectProvider{publicSignalRConnectProvider(){}publicboolIsStopRequested{get;set;}=false;//1-)This is important and default `false`.publicasyncTask<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)]publicpartialclassMessagePerson:ContentPage{SignalRConnectProvider signalR =newSignalRConnectProvider();publicMessagePerson(){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 Abortpada perusahaan Threadobjek. 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.
The Thread.Abortmetode 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, panggilan Thread.Abortdapat mencegah konstruktor statis dari mengeksekusi atau mencegah pelepasan sumber daya yang tidak dikelola.
Kode ini gagal dengan pengecualian runtime: System.InvalidOperationException: RunSynchronously mungkin tidak dipanggil pada tugas yang sudah dimulai.
Theodor Zoulias
1
@TheodorZoulias Tangkapan yang bagus. Terima kasih. Saya memperbaiki kode dan umumnya memperbaiki jawabannya.
Edward Brey
1
Ya, ini memperbaiki bug. Peringatan lain yang mungkin harus disebutkan adalah yang Thread.Aborttidak didukung pada .NET Core. Mencoba menggunakannya di sana menghasilkan pengecualian: System.PlatformNotSupportedException: Thread abort tidak didukung pada platform ini. Peringatan ketiga adalah bahwa SingleThreadTaskSchedulertidak dapat digunakan secara efektif dengan tugas gaya janji, dengan kata lain dengan tugas yang dibuat dengan asyncdelegasi. Misalnya tertanam await Task.Delay(1000)berjalan di utas, jadi tidak terpengaruh menjadi acara utas.
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:
sumber
Task.Wait(TimeSpan / int)
untuk memberikan batas waktu (berdasarkan waktu) dari luar.Task
? Sesuatu seperti:public int StartNewTask(Action method)
. Di dalamStartNewTask
metode yang saya membuat yang baruTask
dengan:Task task = new Task(() => method()); task.Start();
. Jadi bagaimana saya bisa mengelolaCancellationToken
? Saya juga ingin tahu jika inginThread
menerapkan logika untuk memeriksa apakah ada beberapa Tugas yang masih menggantung dan bunuh mereka kapanForm.Closing
. DenganThreads
saya gunakanThread.Abort()
.Membatalkan tugas dapat dilakukan dengan mudah jika Anda menangkap utas tempat tugas berjalan. Berikut adalah contoh kode untuk menunjukkan ini:
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.
sumber
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 apakahSeperti yang disarankan pos ini , ini dapat dilakukan dengan cara berikut:
Meskipun berhasil, tidak disarankan untuk menggunakan pendekatan seperti itu. Jika Anda dapat mengontrol kode yang menjalankan tugas, Anda sebaiknya menggunakan penanganan pembatalan yang benar.
sumber
Hal semacam ini adalah salah satu alasan logistik mengapa
Abort
usang. Pertama dan terutama, jangan gunakanThread.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
.sumber
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 samaCancellationTokenSource
dapat digunakan untuk memicu pembatalan semua tugas Anda dan utas utama Anda.Ini akan mengarah pada desain yang jauh lebih sederhana, dan lebih aman.
sumber
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
sumber
Saya menggunakan pendekatan campuran untuk membatalkan tugas.
Lihat contoh di bawah ini:
sumber
Tugas memiliki dukungan kelas satu untuk pembatalan melalui token pembatalan . Buat tugas Anda dengan token pembatalan, dan batalkan tugas melalui ini secara eksplisit.
sumber
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
sumber
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.
sumber
Saya mencoba
CancellationTokenSource
tetapi saya tidak bisa melakukan ini. Dan saya melakukan ini dengan cara saya sendiri. Dan itu berhasil.Dan kelas lain yang memanggil metode:
sumber
Anda dapat membatalkan tugas seperti benang jika Anda dapat menyebabkan tugas yang akan dibuat pada thread sendiri dan memanggil
Abort
pada perusahaanThread
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 AndaQueueTask
, buat utas baru dan gunakan untuk menjalankan tugas. Kemudian, Anda dapat membatalkan utas, yang akan menyebabkan tugas selesai dalam keadaan rusak dengan aThreadAbortException
.Gunakan penjadwal tugas ini:
Mulai tugas Anda seperti ini:
Kemudian, Anda dapat membatalkan dengan:
Perhatikan bahwa peringatan tentang pembatalan utas masih berlaku:
sumber
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 bahwaSingleThreadTaskScheduler
tidak dapat digunakan secara efektif dengan tugas gaya janji, dengan kata lain dengan tugas yang dibuat denganasync
delegasi. Misalnya tertanamawait Task.Delay(1000)
berjalan di utas, jadi tidak terpengaruh menjadi acara utas.