NET mendefinisikan fungsionalitas multi-threading di namespace System.Threading. Oleh karena itu, untuk menggunakan multithreading, Anda harus mendeklarasikan referensi ke namespace ini terlebih dahulu (menggunakan System.Threading;).
a. Memulai thread. Seperti namanya, "memulai thread" berarti membuat dan memulai thread.
Thread thread1 = Thread baru(ThreadStart baru(Hitungan));
Dimana Count adalah fungsi yang akan dieksekusi oleh thread baru.
b. Matikan benangnya
"Membunuh thread" adalah untuk menghilangkan thread. Agar tidak menyia-nyiakan usaha Anda, yang terbaik adalah menentukan apakah thread tersebut masih hidup (melalui atribut IsAlive) sebelum mematikan thread, dan kemudian memanggil metode Batalkan untuk mematikan thread. .
c. Menjeda thread berarti membiarkan thread yang sedang berjalan tertidur selama jangka waktu tertentu. Misalnya, thread.Sleep(1000); membiarkan thread tidur selama 1 detik.
d.Prioritas tidak memerlukan penjelasan. Atribut threadPriority di kelas Thread digunakan untuk mengatur prioritas, tetapi tidak ada jaminan bahwa sistem operasi akan menerima prioritas tersebut. Prioritas suatu thread dapat dibagi menjadi 5 jenis: Normal, Di AtasNormal, Di BawahNormal, Tertinggi, dan Terendah. Contoh implementasi spesifiknya adalah sebagai berikut:
thread.Priority = ThreadPriority.Tertinggi;
e.Tangguhkan benang
Metode Suspend dari kelas Thread digunakan untuk menangguhkan thread hingga Resume dipanggil sebelum thread dapat melanjutkan eksekusi. Jika thread sudah ditangguhkan, itu tidak akan berhasil.
if (utas.ThreadState = ThreadState.Berjalan)
{
thread.Suspend();
}
f. Thread pemulihan digunakan untuk melanjutkan thread yang ditangguhkan sehingga dapat terus dijalankan. Jika thread tidak ditangguhkan, maka thread tersebut tidak akan berfungsi.
if (thread.ThreadState = ThreadState.Suspended)
{
thread.Resume();
}
Sebuah contoh tercantum di bawah ini untuk mengilustrasikan fungsionalitas threading sederhana. Contoh ini berasal dari dokumentasi bantuan.
menggunakan Sistem;
menggunakan Sistem.Threading;
// Skenario threading sederhana: Mulai menjalankan metode statis
// pada thread kedua.
Contoh Thread kelas publik {
// Metode ThreadProc dipanggil saat thread dimulai.
// Ini berulang sepuluh kali, menulis ke konsol dan menghasilkan
// sisa waktunya dipotong setiap kali, lalu diakhiri.
kekosongan statis publik ThreadProc() {
untuk (int saya = 0; saya < 10; saya++) {
Console.WriteLine("ThreadProc: {0}", i);
// Hasilkan sisa waktu.
Thread.Tidur(0);
}
}
public static void Utama() {
Console.WriteLine("Thread utama: Mulai thread kedua.");
// Konstruktor untuk kelas Thread memerlukan ThreadStart
// delegasi yang mewakili metode yang akan dieksekusi pada
// thread.C# menyederhanakan pembuatan delegasi ini.
Thread t = Thread baru(ThreadStart baru(ThreadProc));
// Mulai ThreadProc. Pada uniprosesor, utas tidak masuk
// waktu prosesor apa pun hingga thread utama menghasilkan
// Thread.Sleep setelah t.Start() untuk melihat perbedaannya.
t.Mulai();
//Thread.Tidur(0);
untuk (int saya = 0; saya < 4; saya++) {
Console.WriteLine("Utas utama: Lakukan beberapa pekerjaan.");
Thread.Tidur(0);
}
Console.WriteLine("Utas utama: Panggil Gabung(), untuk menunggu hingga ThreadProc berakhir.");
t.Gabung();
Console.WriteLine("Thread utama: ThreadProc.Join telah kembali. Tekan Enter untuk mengakhiri program.");
Konsol.ReadLine();
}
}
Kode ini menghasilkan keluaran yang mirip dengan berikut:
Thread utama: Mulai thread kedua.
Utas utama: Lakukan beberapa pekerjaan.
Proses Utas: 0
Utas utama: Lakukan beberapa pekerjaan.
Proses Thread: 1
Utas utama: Lakukan beberapa pekerjaan.
Proses Thread: 2
Utas utama: Lakukan beberapa pekerjaan.
Proses Thread: 3
Thread utama: Panggil Join(), untuk menunggu hingga ThreadProc berakhir.
Proses Thread: 4
Proses Thread: 5
Proses Thread: 6
Proses Thread: 7
Proses Thread: 8
Proses Thread: 9
Thread utama: ThreadProc.Join telah kembali. Tekan Enter untuk mengakhiri program.
Di Visul C#, namespace System.Threading menyediakan beberapa kelas dan antarmuka yang memungkinkan pemrograman multi-thread. Ada tiga metode untuk membuat thread: Thread, ThreadPool, dan Timer. Berikut adalah pengenalan singkat tentang cara menggunakannya satu per satu.
1. Benang
Ini mungkin metode yang paling rumit, namun memberikan berbagai kontrol fleksibel atas thread. Pertama, Anda harus menggunakan konstruktornya untuk membuat instance thread. Parameternya relatif sederhana, dengan hanya satu delegasi ThreadStart: public Thread(ThreadStart start); lalu panggil Start() untuk memulainya untuk mengatur atau Mendapatkan prioritas berjalannya (enum ThreadPriority: Normal, Terendah, Tertinggi, Di BawahNormal, Di AtasNormal).
Contoh berikut pertama-tama menghasilkan dua instance thread t1 dan t2, kemudian menetapkan prioritasnya masing-masing, dan kemudian memulai kedua thread tersebut (kedua thread pada dasarnya sama, hanya saja outputnya berbeda, t1 adalah "1" dan t2 adalah "2 ", Berdasarkan rasio jumlah karakter yang dihasilkannya, secara kasar kita dapat melihat rasio waktu CPU yang digunakan, yang juga mencerminkan prioritasnya masing-masing).
kekosongan statis Utama (string[] args)
{
Thread t1 = Thread baru(ThreadStart baru(Thread1));
Thread t2 = Thread baru(ThreadStart baru(Thread2));
t1.Prioritas = ThreadPriority.BelowNormal;
t2.Prioritas = ThreadPriority.Terendah;
t1.Mulai();
t2.Mulai();
}
kekosongan statis publik Thread1()
{
untuk (int saya = 1; saya < 1000; saya++)
{//Tulis "1" setiap kali loop dijalankan.
dosth();
Konsol.Tulis("1");
}
}
kekosongan statis publik Thread2()
{
untuk (int saya = 0; saya < 1000; saya++)
{//Tuliskan "2" setiap kali loop dijalankan.
dosth();
Konsol.Tulis("2");
}
}
kekosongan statis publik dosth()
{//Digunakan untuk menyimulasikan operasi yang kompleks
untuk (int j = 0; j < 10000000; j++)
{
ke dalam a=15;
a = a*a*a*a;
}
}
Hasil dari menjalankan program di atas adalah:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
Dari hasil di atas, kita dapat melihat bahwa thread t1 memakan waktu CPU lebih banyak daripada t2. Hal ini karena prioritas t1 lebih tinggi daripada t2. Jika kita mengatur prioritas t1 dan t2 ke Normal, maka hasilnya akan sama adalah seperti gambar berikut:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
Dari contoh di atas, kita dapat melihat bahwa strukturnya mirip dengan thread pekerja win32, tetapi lebih sederhana Anda hanya perlu menggunakan fungsi yang akan dipanggil oleh thread sebagai delegasi, dan kemudian menggunakan delegasi sebagai parameter membuat instance thread. Ketika Start() dipanggil untuk memulai, fungsi terkait akan dipanggil, dan eksekusi akan dimulai dari baris pertama fungsi tersebut.
Selanjutnya, kami menggabungkan properti ThreadState dari thread untuk memahami kontrol thread. ThreadState adalah tipe enumerasi yang mencerminkan keadaan thread. Saat instance Thread pertama kali dibuat, ThreadState-nya Belum Dimulai; ketika thread dimulai dengan memanggil Start(), ThreadState-nya sedang Berjalan setelah thread dimulai, jika Anda ingin thread tersebut dijeda (diblokir), Anda dapat memanggil Thread. Metode Sleep(), ia memiliki dua metode yang kelebihan beban (Sleep(int), Sleep(Timespan)), yang hanya merupakan format berbeda untuk mewakili jumlah waktu. Ketika fungsi ini dipanggil dalam sebuah thread, itu berarti thread tersebut akan Memblokir untuk jangka waktu tertentu (waktu ditentukan oleh jumlah milidetik atau Rentang Waktu yang diteruskan ke Tidur, tetapi jika parameternya 0, itu berarti menangguhkan thread ini untuk mengaktifkan thread lain untuk dieksekusi, menentukan Infinite untuk memblokir thread tanpa batas), di kali ini ThreadState-nya Akan menjadi WaitSleepJoin. Hal lain yang perlu diperhatikan adalah fungsi Sleep() didefinisikan sebagai statis? ! Ini juga berarti bahwa ini tidak dapat digunakan bersama dengan instance thread, yaitu, tidak ada panggilan yang mirip dengan t1.Sleep(10)! Sama seperti ini, fungsi Sleep() hanya bisa dipanggil oleh thread yang perlu "Tidur" itu sendiri, dan tidak boleh dipanggil oleh thread lain. Sama seperti kapan harus Tidur adalah masalah pribadi yang tidak bisa diputuskan oleh orang lain . Namun ketika thread berada dalam status WaitSleepJoin dan harus membangunkannya, Anda dapat menggunakan metode Thread.Interrupt, yang akan menampilkan ThreadInterruptedException pada thread. Mari kita lihat contohnya terlebih dahulu (perhatikan metode pemanggilan Sleep):
kekosongan statis Utama (string[] args)
{
Thread t1 = Thread baru(ThreadStart baru(Thread1));
t1.Mulai();
t1.Interupsi();
E.WaitOne();
t1.Interupsi();
t1.Gabung();
Console.WriteLine("t1 sudah berakhir");
}
AutoResetEvent statis E = AutoResetEvent baru (salah);
kekosongan statis publik Thread1()
{
mencoba
{//Terlihat dari parameter yang akan menyebabkan hibernasi
Thread.Sleep(Waktu habis.Tak Terbatas);
}
menangkap(Sistem.Threading.ThreadInterruptedException e)
{//Penangani interupsi
Console.WriteLine ("interupsi pertama");
}
E.Set();
mencoba
{// tidur
Thread.Sleep(Waktu habis.Tak Terbatas);
}
menangkap(Sistem.Threading.ThreadInterruptedException e)
{
Console.WriteLine (" interupsi ke-2");
}//Jeda selama 10 detik
Thread.Tidur (10000);
}
Hasil yang berjalan adalah: interupsi pertama
interupsi ke-2
(Setelah 10 detik)t1 berakhir
Dari contoh di atas, kita dapat melihat bahwa metode Thread.Interrupt dapat membangunkan program dari status pemblokiran (WaitSleepJoin) dan memasukkan penangan interupsi yang sesuai, lalu melanjutkan eksekusi (ThreadState-nya juga berubah menjadi Running). Fungsi harus memperhatikan hal-hal berikut:
1. Cara ini tidak hanya dapat membangunkan pemblokiran yang disebabkan oleh Sleep, tetapi juga efektif untuk semua metode yang dapat menyebabkan thread memasuki status WaitSleepJoin (seperti Wait dan Join). Seperti yang ditunjukkan pada contoh di atas, saat menggunakannya, Anda harus memasukkan metode yang menyebabkan pemblokiran thread ke dalam blok try, dan memasukkan pengendali interupsi yang sesuai ke dalam blok catch.
2. Panggil Interupsi pada thread tertentu. Jika dalam status WaitSleepJoin, maka akan masuk ke penangan interupsi yang sesuai untuk dieksekusi. Jika saat ini tidak dalam status WaitSleepJoin, maka akan segera diinterupsi saat memasuki status ini nanti . Jika Interrupt dipanggil beberapa kali sebelum interupsi, hanya panggilan pertama yang valid. Inilah sebabnya saya menggunakan sinkronisasi pada contoh di atas, untuk memastikan bahwa panggilan kedua ke Interrupt dipanggil setelah interupsi pertama, jika tidak maka akan menyebabkan interupsi kedua. Panggilan tersebut tidak berpengaruh (jika dipanggil sebelum interupsi pertama). Anda dapat mencoba menghapus sinkronisasi. Kemungkinan hasilnya adalah: interupsi pertama
Contoh di atas juga menggunakan dua metode lain untuk membuat thread memasuki status WaitSleepJoin: menggunakan objek sinkronisasi dan metode Thread.Join. Penggunaan metode Gabung relatif sederhana. Artinya, thread saat ini yang memanggil metode ini diblokir hingga thread lain (t1 dalam contoh ini) berakhir atau waktu yang ditentukan berlalu (jika juga memerlukan parameter waktu). kondisi (jika ada) terjadi, maka segera mengakhiri status WaitSleepJoin dan memasuki status Berjalan (kondisi dapat ditentukan berdasarkan nilai kembalian metode .Join. Jika benar, thread dihentikan; jika salah, waktunya sudah habis). Thread juga dapat ditangguhkan menggunakan metode Thread.Suspend. Ketika sebuah thread berada dalam status Running dan metode Suspend dipanggil, maka thread tersebut akan memasuki status SuspendRequested, namun thread tersebut tidak akan langsung ditangguhkan hingga thread tersebut mencapai brankas. titik. Thread hang, pada titik ini thread akan memasuki status Suspended. Jika dipanggil pada thread yang sudah ditangguhkan, maka thread tersebut tidak valid. Untuk melanjutkan operasi, panggil saja Thread.Resume.
Hal terakhir yang kita bicarakan adalah penghancuran thread. Kita dapat memanggil metode Abort pada thread yang perlu dihancurkan, dan metode tersebut akan memunculkan ThreadAbortException pada thread ini. Kita dapat memasukkan beberapa kode di thread ke dalam blok try, dan memasukkan kode pemrosesan yang sesuai ke dalam blok catch yang sesuai. Ketika thread mengeksekusi kode di blok try, jika Abort dipanggil, maka thread tersebut akan melompat ke blok catch yang sesuai. .Dieksekusi dalam blok catch, ini akan berhenti setelah mengeksekusi kode di blok catch (jika ResetAbort dijalankan dalam blok catch, maka akan berbeda: ini akan membatalkan permintaan Abort saat ini dan terus mengeksekusi ke bawah. Oleh karena itu, jika Anda ingin memastikan bahwa thread berakhir, yang terbaik adalah menggunakan Join , seperti pada contoh di atas).
2. Kumpulan Benang
Thread Pool (ThreadPool) adalah metode yang relatif sederhana. Cocok untuk tugas-tugas pendek yang memerlukan banyak thread (seperti beberapa thread yang sering diblokir). Kerugiannya adalah tidak dapat mengontrol thread yang dibuat. Karena setiap proses hanya memiliki satu kumpulan thread, dan tentu saja setiap domain aplikasi hanya memiliki satu kumpulan thread (baris), Anda akan menemukan bahwa fungsi anggota kelas ThreadPool semuanya statis! Saat Anda memanggil ThreadPool.QueueUserWorkItem, ThreadPool.RegisterWaitForSingleObject, dll. untuk pertama kalinya, instance kumpulan thread akan dibuat. Berikut ini adalah pengenalan dua fungsi di kumpulan thread:
public static bool QueueUserWorkItem( //Mengembalikan nilai true jika panggilan berhasil
WaitCallback callBack,//Delegasi dipanggil oleh thread yang akan dibuat
status objek //Parameter diteruskan ke delegasi
)//Fungsi kelebihan beban lainnya serupa, hanya saja delegasi tidak mengambil parameter apa pun. Fungsi dari fungsi ini adalah mengantri thread yang akan dibuat ke kumpulan thread ketika jumlah thread yang tersedia di kumpulan thread tidak nol ( kumpulan thread telah membuat thread. Jika jumlahnya terbatas (nilai defaultnya adalah 25), thread ini akan dibuat. Jika tidak, thread ini akan diantrekan ke kumpulan thread dan menunggu hingga thread tersedia.
publik statis RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,//WaitHandle untuk didaftarkan
Panggilan Balik WaitOrTimerCallback, // Delegasi panggilan rangkaian pesan
status objek,//Parameter diteruskan ke delegasi
int TimeOut,//Timeout, satuannya adalah milidetik,
bool mengeksekusiOnlyOnce file:// Apakah akan mengeksekusi hanya sekali
);
delegasi publik batal WaitOrTimerCallback(
status objek,//yaitu, parameter yang diteruskan ke delegasi
bool timedOut//true berarti karena panggilan timeout, jika tidak maka berarti waitObject
);
Fungsi dari fungsi ini adalah untuk membuat thread tunggu. Setelah fungsi ini dipanggil, thread ini akan berada dalam status "diblokir" hingga parameter waitObject berubah ke status dihentikan atau waktu TimeOut yang ditentukan tiba Perlu dicatat bahwa "Pemblokiran" ini sangat berbeda dengan status WaitSleepJoin dari Thread: ketika sebuah Thread berada dalam status WaitSleepJoin, CPU akan membangunkannya secara teratur untuk melakukan polling dan memperbarui informasi status, lalu memasukkan status WaitSleepJoin lagi. Peralihan thread sangat intensif sumber daya; dan Thread yang dibuat dengan fungsi ini berbeda. CPU tidak akan beralih ke thread ini sebelum dipicu untuk dijalankan tahu kapan harus menjalankannya? Faktanya, kumpulan thread akan menghasilkan beberapa thread tambahan untuk memantau kondisi pemicu ini. Setelah kondisi terpenuhi, thread yang sesuai akan dimulai. Tentu saja, thread tambahan itu sendiri juga memerlukan waktu, tetapi jika Anda perlu membuat lebih banyak menunggu utas, gunakan kumpulan utas. Keuntungannya menjadi lebih jelas. Lihat contoh di bawah ini:
static AutoResetEvent ev=AutoResetEvent baru(false);
int statis publik Utama(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
setiap,
WaitOrTimerCallback baru (WaitThreadFunc),
4,
2000,
false//Menunjukkan bahwa pengatur waktu akan direset setiap kali operasi menunggu selesai hingga logout dan menunggu.
);
ThreadPool.QueueUserWorkItem (WaitCallback baru (ThreadFunc),8);
Thread.Tidur (10000);
kembali 0;
}
public static void ThreadFunc(objek b)
{ Console.WriteLine ("objeknya adalah {0}",b);
untuk(int i=0;i<2;i++)
{ Thread.Tidur (1000);
ev.Set();
}
}
public static void WaitThreadFunc(objek b,bool t)
{ Console.WriteLine ("objeknya adalah {0},t adalah {1}",b,t);
}
Hasil pengoperasiannya adalah:
objeknya adalah 8
objeknya 4,t adalah False
objeknya 4,t adalah False
objeknya 4,t Benar
objeknya 4,t Benar
objeknya 4,t Benar
Dari hasil di atas, kita dapat melihat bahwa thread ThreadFunc dijalankan satu kali, dan WaitThreadFunc dijalankan sebanyak 5 kali. Kita dapat menilai alasan memulai thread ini dari parameter bool t di WaitOrTimerCallback: jika t salah, berarti waitObject, jika tidak maka karena batas waktu. Selain itu, kita juga dapat meneruskan beberapa parameter ke thread melalui objek b.
3. pengatur waktu
Cocok untuk metode yang perlu dipanggil secara berkala. Ini tidak berjalan di thread yang membuat pengatur waktu. Ini berjalan di thread terpisah yang secara otomatis dialokasikan oleh sistem. Ini mirip dengan metode SetTimer di Win32. Strukturnya adalah:
pengatur waktu publik(
Panggilan balik TimerCallback,//Metode yang akan dipanggil
status objek,//parameter diteruskan ke panggilan balik
int dueTime,//Berapa lama waktu yang dibutuhkan untuk mulai memanggil panggilan balik?
int period//Interval waktu untuk memanggil metode ini
); // Jika dueTime adalah 0, callback akan segera mengeksekusi panggilan pertamanya. Jika dueTime adalah Infinite, callback tidak memanggil metodenya. Pengatur waktu dinonaktifkan, namun dapat diaktifkan kembali menggunakan metode Ubah. Jika periodenya 0 atau Tak Terbatas, dan dueTime bukan Tak Terbatas, callback akan memanggil metodenya satu kali. Perilaku periodik pengatur waktu dinonaktifkan, namun dapat diaktifkan kembali menggunakan metode Ubah. Jika periodenya nol (0) atau Tak Terbatas, dan dueTime bukan Tak Terbatas, callback akan memanggil metodenya satu kali. Perilaku periodik pengatur waktu dinonaktifkan, namun dapat diaktifkan kembali menggunakan metode Ubah.
Jika kita ingin mengubah periode dan waktu jatuh tempo timer setelah membuatnya, kita dapat mengubahnya dengan memanggil metode Ubah Timer:
bool publik Ubah(
int jatuh tempo,
periode int
);//Jelas kedua parameter yang diubah sesuai dengan dua parameter di Timer
int statis publik Utama(string[] args)
{
Console.WriteLine ("periodenya 1000");
Timer tm=Timer baru (TimerCallback baru (TimerCall),3.1000.1000);
Thread.Tidur (2000);
Console.WriteLine ("periodenya 500");
tm.Ubah (0,800);
Thread.Tidur (3000);
kembali 0;
}
TimerCall void statis publik (objek b)
{
Console.WriteLine ("timercallback; b adalah {0}",b);
}
Hasil pengoperasiannya adalah:
periode adalah 1000
pengatur waktu panggilan balik;b adalah 3
pengatur waktu panggilan balik;b adalah 3
periode adalah 500
pengatur waktu panggilan balik;b adalah 3
pengatur waktu panggilan balik;b adalah 3
pengatur waktu panggilan balik;b adalah 3
pengatur waktu panggilan balik;b adalah 3
Meringkaskan
Dari pengenalan singkat di atas, kita dapat melihat kejadian di mana mereka digunakan masing-masing: Thread cocok untuk kejadian yang memerlukan kontrol thread yang rumit; ThreadPool cocok untuk tugas pendek yang memerlukan banyak thread (seperti beberapa thread yang sering diblokir). ); Timer cocok untuk metode yang perlu dipanggil secara berkala. Selama kita memahami karakteristik penggunaannya, kita dapat memilih metode yang tepat dengan baik.