Saya telah membaca dokumentasi tentang ini dan saya pikir saya mengerti. Sebuah AutoResetEvent
me - reset ketika kode melewati event.WaitOne()
, tetapi ManualResetEvent
tidak.
Apakah ini benar?
Saya telah membaca dokumentasi tentang ini dan saya pikir saya mengerti. Sebuah AutoResetEvent
me - reset ketika kode melewati event.WaitOne()
, tetapi ManualResetEvent
tidak.
Apakah ini benar?
Jawaban:
Iya. Ini seperti perbedaan antara jalan tol dan pintu. The ManualResetEvent
adalah pintu, yang perlu ditutup (ulang) secara manual. Ini AutoResetEvent
adalah jalan tol, memungkinkan satu mobil lewat dan secara otomatis menutup sebelum mobil berikutnya bisa lewat.
Bayangkan saja yang AutoResetEvent
dieksekusi WaitOne()
dan Reset()
sebagai operasi atom tunggal.
Jawaban singkatnya adalah ya. Perbedaan yang paling penting adalah bahwa AutoResetEvent hanya akan mengizinkan satu utas menunggu untuk melanjutkan. ManualResetEvent di sisi lain akan terus membiarkan utas, beberapa bahkan pada saat yang sama, untuk berlanjut sampai Anda menyuruhnya berhenti (Setel ulang).
Diambil dari buku singkat C # 3.0, karya Joseph Albahari
Threading dalam C # - E-Book Gratis
ManualResetEvent adalah variasi pada AutoResetEvent. Ini berbeda karena tidak secara otomatis mengatur ulang setelah utas melalui panggilan WaitOne, dan berfungsi seperti gerbang: memanggil Set membuka gerbang, memungkinkan sejumlah utas apa pun yang ditunggu WaitOne di gerbang; panggilan Atur Ulang menutup gerbang, menyebabkan, berpotensi, antrian para pelayan menumpuk hingga pintu berikutnya dibuka.
Seseorang dapat mensimulasikan fungsi ini dengan bidang "gateOpen" boolean (dideklarasikan dengan kata kunci yang mudah menguap) dalam kombinasi dengan "spin-sleeping" - berulang kali memeriksa tanda, dan kemudian tidur untuk waktu yang singkat.
ManualResetEvents kadang-kadang digunakan untuk memberi sinyal bahwa operasi tertentu selesai, atau inisialisasi thread selesai dan siap untuk melakukan pekerjaan.
Saya membuat contoh sederhana untuk memperjelas pemahaman dari ManualResetEvent
vs AutoResetEvent
.
AutoResetEvent
: mari kita asumsikan Anda memiliki 3 thread pekerja. Jika salah satu utas tersebut akan memanggil WaitOne()
2 utas lainnya, akan menghentikan eksekusi dan menunggu sinyal. Saya berasumsi mereka menggunakan WaitOne()
. Ini seperti; jika saya tidak bekerja, tidak ada yang bekerja. Dalam contoh pertama Anda bisa melihatnya
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Ketika Anda memanggil Set()
semua utas akan bekerja dan menunggu sinyal. Setelah 1 detik saya mengirim sinyal kedua dan mereka mengeksekusi dan menunggu ( WaitOne()
). Pikirkan orang-orang ini adalah pemain tim sepak bola dan jika satu pemain mengatakan saya akan menunggu sampai manajer memanggil saya, dan yang lain akan menunggu sampai manajer memberitahu mereka untuk melanjutkan ( Set()
)
public class AutoResetEventSample
{
private AutoResetEvent autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
autoReset.WaitOne();
}
}
}
Dalam contoh ini Anda dapat dengan jelas melihat bahwa ketika Anda menekannya pertama kali Set()
akan melepaskan semua utas, lalu setelah 1 detik memberi sinyal semua utas untuk menunggu! Segera setelah Anda mengaturnya lagi terlepas dari apa yang mereka panggil WaitOne()
di dalam, mereka akan tetap berjalan karena Anda harus menelepon secara manual Reset()
untuk menghentikan semuanya.
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
Ini lebih tentang hubungan Wasit / Pemain di sana terlepas dari salah satu pemain yang cedera dan menunggu bermain orang lain akan terus bekerja. Jika Wasit mengatakan tunggu ( Reset()
) maka semua pemain akan menunggu sampai sinyal berikutnya.
public class ManualResetEventSample
{
private ManualResetEvent manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();
Console.WriteLine("Main thread reached to end.");
}
public void Worker1()
{
Console.WriteLine("Entered in worker 1");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker1 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker2()
{
Console.WriteLine("Entered in worker 2");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker2 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
public void Worker3()
{
Console.WriteLine("Entered in worker 3");
for (int i = 0; i < 5; i++) {
Console.WriteLine("Worker3 is running {0}", i);
Thread.Sleep(2000);
manualReset.WaitOne();
}
}
}
autoResetEvent.WaitOne()
mirip dengan
try
{
manualResetEvent.WaitOne();
}
finally
{
manualResetEvent.Reset();
}
sebagai operasi atom
OK, biasanya bukan praktik yang baik untuk menambahkan 2 jawaban di utas yang sama, tetapi saya tidak ingin mengedit / menghapus jawaban saya sebelumnya, karena dapat membantu dengan cara lain.
Sekarang, saya membuat cuplikan aplikasi konsol yang jauh lebih komprehensif dan mudah dimengerti, di bawah ini.
Cukup jalankan contoh di dua konsol yang berbeda, dan amati perilaku. Anda akan mendapatkan gagasan yang lebih jelas tentang apa yang terjadi di balik layar.
Atur Ulang Manual
using System;
using System.Threading;
namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
public class ManualResetEventSample
{
private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
Thread.Sleep(15000);
Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Set();
Thread.Sleep(2000);
Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
Thread.Sleep(5000);
_manualReset.Reset();
Thread.Sleep(2000);
Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
Thread.Sleep(10000);
Console.WriteLine();
Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
Thread.Sleep(5000);
Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker1()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker2()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker3()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);
// this gets blocked until _autoReset gets signal
_manualReset.WaitOne();
}
Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
}
}
Atur Ulang Otomatis Acara
using System;
using System.Threading;
namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
public class AutoResetEventSample
{
private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
Thread.Sleep(15000);
Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Set();
Thread.Sleep(2000);
Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
Thread.Sleep(5000);
_autoReset.Reset();
Thread.Sleep(2000);
Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
Thread.Sleep(10000);
Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
Thread.Sleep(5000);
Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker1()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker2()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
public void Worker3()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
// this gets blocked until _autoReset gets signal
_autoReset.WaitOne();
}
Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
}
}
}
AutoResetEvent mempertahankan variabel boolean dalam memori. Jika variabel boolean salah maka ia memblokir utas dan jika variabel boolean benar, ia membuka blokir utas.
Ketika kita instantiate objek AutoResetEvent, kita melewatkan nilai default dari nilai boolean di konstruktor. Di bawah ini adalah sintaks instantiate objek AutoResetEvent.
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
Metode WaitOne
Metode ini memblokir utas saat ini dan menunggu sinyal oleh utas lainnya. Metode WaitOne menempatkan utas saat ini ke status Sleep thread. Metode WaitOne mengembalikan nilai true jika menerima sinyal yang lain mengembalikan false.
autoResetEvent.WaitOne();
Kelebihan kedua metode WaitOne menunggu jumlah detik yang ditentukan. Jika tidak mendapatkan sinyal, teruskan pekerjaannya.
static void ThreadMethod()
{
while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
{
Console.WriteLine("Continue");
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Console.WriteLine("Thread got signal");
}
Kami memanggil metode WaitOne dengan melewatkan 2 detik sebagai argumen. Dalam loop sementara, ia menunggu sinyal selama 2 detik kemudian melanjutkan kerjanya. Ketika utas menerima sinyal, WaitOne mengembalikan true dan keluar dari loop dan mencetak "Utas mendapat sinyal".
Atur metode
Metode Set AutoResetEvent mengirim sinyal ke utas menunggu untuk melanjutkan kerjanya. Di bawah ini adalah sintaks memanggil metode Set.
autoResetEvent.Set();
ManualResetEvent mempertahankan variabel boolean dalam memori. Ketika variabel boolean salah maka ia memblokir semua utas dan ketika variabel boolean benar, ia membuka blokir semua utas.
Ketika kami instantiate ManualResetEvent, kami menginisialisasi dengan nilai boolean default.
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Dalam kode di atas, kita menginisialisasi ManualResetEvent dengan nilai salah, itu berarti semua utas yang memanggil metode WaitOne akan memblokir sampai beberapa utas memanggil metode Set ().
Jika kita menginisialisasi ManualResetEvent dengan nilai sebenarnya, semua utas yang memanggil metode WaitOne tidak akan diblokir dan bebas untuk melanjutkan lebih jauh.
Metode WaitOne
Metode ini memblokir utas saat ini dan menunggu sinyal oleh utas lainnya. Mengembalikan nilai true jika menerima sinyal yang lain mengembalikan false.
Di bawah ini adalah sintaks memanggil metode WaitOne.
manualResetEvent.WaitOne();
Dalam kelebihan kedua metode WaitOne, kita dapat menentukan interval waktu sampai utas saat ini menunggu sinyal. Jika dalam waktu internal, itu tidak menerima sinyal itu kembali palsu dan masuk ke baris metode berikutnya.
Di bawah ini adalah sintaks memanggil metode WaitOne dengan interval waktu.
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
Kami telah menetapkan 5 detik ke dalam metode WaitOne. Jika objek manualResetEvent tidak menerima sinyal antara 5 detik, itu menetapkan variabel isSignalled menjadi false.
Setel Metode
Metode ini digunakan untuk mengirim sinyal ke semua utas menunggu. Set () Metode mengatur variabel objek boolean ManualResetEvent menjadi true. Semua utas menunggu tidak diblokir dan melangkah lebih jauh.
Di bawah ini adalah sintaks memanggil metode Set ().
manualResetEvent.Set();
Metode Reset
Setelah kita memanggil metode Set () pada objek ManualResetEvent, booleannya tetap benar. Untuk mereset nilai, kita dapat menggunakan metode Reset (). Metode reset mengubah nilai boolean menjadi false.
Di bawah ini adalah sintaks memanggil metode Reset.
manualResetEvent.Reset();
Kita harus segera memanggil metode Reset setelah memanggil metode Set jika kita ingin mengirim sinyal ke utas beberapa kali.
Iya. Ini benar sekali.
Anda bisa melihat ManualResetEvent sebagai cara untuk menunjukkan status. Sesuatu aktif (Set) atau mati (Reset). Suatu kejadian dengan beberapa durasi. Utas apa pun yang menunggu keadaan itu terjadi dapat dilanjutkan.
AutoResetEvent lebih sebanding dengan sinyal. Indikasi satu tembakan bahwa sesuatu telah terjadi. Suatu kejadian tanpa durasi apa pun. Biasanya tetapi tidak selalu "sesuatu" yang telah terjadi kecil dan perlu ditangani oleh utas tunggal - maka pengaturan ulang otomatis setelah satu utas mengkonsumsi acara tersebut.
Ya itu betul.
Anda bisa mendapatkan ide dengan menggunakan keduanya.
Jika Anda perlu memberi tahu bahwa Anda telah selesai dengan beberapa pekerjaan dan lainnya (utas) menunggu ini sekarang dapat dilanjutkan, Anda harus menggunakan ManualResetEvent.
Jika Anda perlu memiliki akses eksklusif bersama ke sumber daya apa pun, Anda harus menggunakan AutoResetEvent.
Jika Anda ingin memahami AutoResetEvent dan ManualResetEvent Anda perlu memahami bukan threading tetapi menyela!
.NET ingin menyulap pemrograman tingkat rendah sejauh mungkin.
Interupsi adalah sesuatu yang digunakan dalam pemrograman tingkat rendah yang sama dengan sinyal yang dari rendah menjadi tinggi (atau sebaliknya). Ketika ini terjadi, program menghentikan eksekusi normal dan memindahkan pointer eksekusi ke fungsi yang menangani acara ini .
Hal pertama yang harus dilakukan ketika interupsi happend adalah mengatur ulang kondisinya, karena perangkat kerasnya bekerja dengan cara ini:
Ini adalah perbedaan antara ManualResetEvent dan AutoResetEvent.
Jika ManualResetEvent terjadi dan saya tidak meresetnya, lain kali itu terjadi saya tidak akan dapat mendengarkannya.