Apa perbedaan antara ManualResetEvent dan AutoResetEvent di .NET?

Jawaban:

920

Iya. Ini seperti perbedaan antara jalan tol dan pintu. The ManualResetEventadalah pintu, yang perlu ditutup (ulang) secara manual. Ini AutoResetEventadalah jalan tol, memungkinkan satu mobil lewat dan secara otomatis menutup sebelum mobil berikutnya bisa lewat.

Dan Goldstein
sumber
166
Itu analogi yang bagus.
twk
Lebih buruk lagi, jangan menunggu terlalu lama untuk mengatur ARE ke WaitOne, atau akan ditempatkan kembali sementara itu. Sudah banyak thread yang ditinggalkan dengan itu.
Oliver Friedrich
24
Atau seperti pintu dan pintu putar.
Constantin
9
Oh, itu sebabnya mereka diberi nama apa adanya.
Arlen Beiler
1
@DanGoldstein, karena ini tidak ditutup dan kalau-kalau ada orang lain menginginkannya: msdn.microsoft.com/en-us/library/…
Phil N DeBlanc
124

Bayangkan saja yang AutoResetEventdieksekusi WaitOne()dan Reset()sebagai operasi atom tunggal.

Michael Damatov
sumber
16
Kecuali bahwa jika Anda menjalankan WaitOne dan Reset sebagai operasi atom tunggal pada acara ManualResetEvent itu masih akan melakukan sesuatu yang berbeda dengan AutoResetEvent. ManualResetEvent melepaskan semua utas menunggu pada saat yang sama, di mana AutoResetEvent menjamin untuk hanya melepaskan satu utas tunggu.
Martin Brown
55

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).

Martin Brown
sumber
36

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.


sumber
19

Saya membuat contoh sederhana untuk memperjelas pemahaman dari ManualResetEventvs 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();
        }
    }
}
Teoman shipahi
sumber
13

autoResetEvent.WaitOne()

mirip dengan

try
{
   manualResetEvent.WaitOne();
}
finally
{
   manualResetEvent.Reset();
}

sebagai operasi atom

vezenkov
sumber
Secara konsep ini hanya benar, tetapi tidak secara praktis. Antara WaitOne dan tombol Reset konteks mungkin terjadi; ini dapat menyebabkan bug halus.
hofingerandi
2
Bisakah Anda memilihnya sekarang? Tidak ada yang praktis akan melakukan blok kode kedua di sini, ini masalah memahami perbedaannya.
vezenkov
11

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 Output Acara Manual

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);
        }
    }

}

Reset Otomatis Output Acara

Teoman shipahi
sumber
ini adalah cara terbaik untuk memahami semuanya, menyalin kode dan menjalankan semuanya sambil mengubah beberapa hal, memahaminya dengan baik sekarang
JohnChris
8

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.

Masoud Siahkali
sumber
7

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.

Boas
sumber
7

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.

Swapnil Patil
sumber
1

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:

  1. pin terhubung ke sinyal dan perangkat keras mendengarkannya berubah (sinyal hanya bisa memiliki dua status).
  2. jika perubahan sinyal berarti sesuatu terjadi dan perangkat keras meletakkan variabel memori ke keadaan terjadi (dan tetap seperti ini bahkan jika sinyal berubah lagi).
  3. pemberitahuan program yang menyatakan perubahan variabel dan memindahkan eksekusi ke fungsi penanganan.
  4. di sini hal pertama yang harus dilakukan, untuk dapat mendengarkan lagi gangguan ini, adalah mengatur ulang variabel memori ini ke keadaan tidak terjadi.

Ini adalah perbedaan antara ManualResetEvent dan AutoResetEvent.
Jika ManualResetEvent terjadi dan saya tidak meresetnya, lain kali itu terjadi saya tidak akan dapat mendengarkannya.

Pangeran
sumber