.NET'te ManualResetEvent ve AutoResetEvent arasındaki fark nedir?


Yanıtlar:


920

Evet. Gişe kapısı ve kapı arasındaki fark gibi. ManualResetEventManuel (reset) kapatılması gereken kapı vardır. Bu AutoResetEvent, bir arabanın geçmesine ve bir sonrakinin geçebilmesi için otomatik olarak kapanmasına izin veren bir geçiş ücretidir.


166
Bu harika bir benzetme.
twk

Daha da kötüsü, ARE'yi WaitOne'a ayarlamak için uzun süre beklemeyin, aksi takdirde sıfırlanır. Birçok terk edilmiş konu vardı.
Oliver Friedrich

24
Ya da bir kapı ve bir turnike gibi.
Constantin

9
Oh, işte bu yüzden ne olduklarını adlandırıyorlar.
Arlen Beiler

1
@DanGoldstein iyi, çünkü bu kapalı değil ve başka birinin istediği durumlarda: msdn.microsoft.com/en-us/library/…
Phil N DeBlanc

124

Sadece AutoResetEventyürütür WaitOne()ve Reset()tek bir atomik işlem olarak düşünün .


16
Bir ManualResetEvent olayında WaitOne ve Reset komutlarını tek bir atomik işlem olarak uyguladıysanız, yine de AutoResetEvent'ten farklı bir şey yapar. ManualResetEvent, tüm bekleyen iş parçacıklarını aynı anda serbest bırakır; burada AutoResetEvent yalnızca bir bekleyen iş parçacığını serbest bırakmayı garanti eder.
Martin Brown

55

Kısa cevap evet. En önemli fark, bir AutoResetEvent öğesinin yalnızca bir bekleyen iş parçacığının devam etmesine izin vermesidir. Öte yandan bir ManualResetEvent, aynı anda birkaç iş parçacığının da durmasını söyleyene kadar devam etmesini sağlar (Sıfırla).


36

C # 3.0 Nutshell kitabından alınmıştır, Joseph Albahari

C # iş parçacığı - Ücretsiz e-kitap

ManualResetEvent, AutoResetEvent öğesinin bir varyasyonudur. Bir WaitOne çağrısında bir iş parçacığına izin verildikten sonra otomatik olarak sıfırlanmamasından farklıdır ve bu nedenle bir kapı gibi çalışır: Set çağrısı kapıyı açar, WaitOne'un kapıda herhangi bir sayıda iş parçacığına izin verir; Sıfırlama çağrısı kapıyı kapatır ve potansiyel olarak bir sonraki açılana kadar garsonlar kuyruğunun birikmesine neden olur.

Bu işlevsellik, "spin-sleeping" ile birlikte bir boolean "gateOpen" alanı (uçucu anahtar sözcük ile bildirildi) ile simüle edilebilir - bayrağını tekrar tekrar kontrol edip kısa bir süre uyuyabilir.

ManualResetEvents bazen belirli bir işlemin tamamlandığını veya bir iş parçacığının tamamlandığını ve çalışmaya hazır olduğunu bildirmek için kullanılır.


19

ManualResetEventVs anlayışını netleştirmek için basit örnekler oluşturdum AutoResetEvent.

AutoResetEvent: varsayalım 3 işçi iplik var. Bu iş parçacıklarından herhangi biri WaitOne()diğer 2 iş parçacığını çağırırsa yürütmeyi durdurur ve sinyal bekler. Onların kullandığını varsayıyorum WaitOne(). Sanki; eğer çalışmazsam kimse çalışmaz. İlk örnekte şunu görebilirsiniz:

autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();

Aradığınızda Set()tüm iş parçacıkları çalışır ve sinyal bekler. 1 saniye sonra ikinci sinyal gönderiyorum ve yürütüyorlar ve bekliyorlar ( WaitOne()). Bu adamların futbol takımı oyuncuları olduğunu düşünün ve bir oyuncu menajer beni arayacak kadar bekleyeceğim, diğerleri ise menajer devam etmelerini söyleyene kadar bekleyecek ( 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();
        }
    }
}

Bu örnekte, ilk vurduğunuzda Set()tüm evrelerin gitmesine izin vereceğini açıkça görebilirsiniz, 1 saniye sonra tüm evreleri beklemeye işaret eder! Bunları WaitOne()içeri çağırdıklarına bakılmaksızın yeniden ayarladığınız anda , çalışmaya devam ederler, çünkü Reset()hepsini durdurmak için manuel olarak aramanız gerekir.

manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();

Hakem / Oyuncu ilişkisi hakkında daha çok o oyuncu ne olursa olsun sakatlanır ve başkalarının oynamaya devam etmesini beklemeye devam eder. Hakem bekle ( Reset()) diyorsa, tüm oyuncular bir sonraki sinyale kadar bekler.

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

13

autoResetEvent.WaitOne()

benzer

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

atomik bir işlem olarak


Bu sadece kavramsal olarak doğrudur, ancak pratik olarak değil. WaitOne ile Sıfırlama arasında bir bağlam anahtarı oluşabilir; bu ince hatalara yol açabilir.
hofingerandi

2
Şimdi oy verebilir misiniz? Hiç kimse burada ikinci kod bloğunu pratikte yapmayacak, bu farkı anlamak meselesi.
vezenkov

11

Tamam, normalde aynı konuya 2 cevap eklemek için iyi bir uygulama değildir, ancak başka bir şekilde yardımcı olabileceğinden önceki cevabımı düzenlemek / silmek istemedim.

Şimdi, aşağıda, çok daha kapsamlı ve kolay anlaşılır, öğrenmesi kolay konsol uygulama snippet'i oluşturdum.

Örnekleri iki farklı konsolda çalıştırın ve davranışı gözlemleyin. Perde arkasında neler olduğu hakkında çok daha net bir fikir edineceksiniz.

Manuel Sıfırlama Etkinliği

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

}

Olay Çıkışını Manuel Sıfırlama

Etkinliği Otomatik Sıfırla

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

}

Olay Çıkışını Otomatik Sıfırla


Bu, hepsini anlamanın, kodu kopyalayıp birkaç şeyi değiştirirken hepsini çalıştırmanın en iyi yoluydu, şimdi iyi anladı
JohnChris

8

AutoResetEvent , bellekte bir boole değişkenini korur. Boolean değişkeni false olursa, iş parçacığını engeller ve boolean değişkeni true olursa, iş parçacığının engellemesini kaldırır.

Bir AutoResetEvent nesnesini somutlaştırdığımızda, yapıcıda boole değerinin varsayılan değerini geçiririz. Aşağıda bir AutoResetEvent nesnesinin örneğinin sözdizimi verilmiştir.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOne yöntemi

Bu yöntem mevcut iş parçacığını engeller ve başka bir iş parçacığının sinyalini bekler. WaitOne yöntemi, geçerli iş parçacığını Uyku iş parçacığı durumuna getirir. WaitOne yöntemi, sinyali alırsa true değerini, false değerini döndürür.

autoResetEvent.WaitOne();

WaitOne yönteminin ikinci aşırı yüklenmesi belirtilen saniye kadar bekleyin. Sinyal almazsa, iş parçacığı çalışmaya devam eder.

static void ThreadMethod()
{
    while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
    {
        Console.WriteLine("Continue");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }

    Console.WriteLine("Thread got signal");
}

Bağımsız değişken olarak 2 saniyeyi geçerek WaitOne yöntemini çağırdık. While döngüsünde, sinyali 2 saniye bekledikten sonra çalışmaya devam eder. İş parçacığı sinyali aldığında WaitOne true değerini döndürür ve döngüden çıkar ve "İş parçacığı sinyali alır" yazısını alır.

Set yöntemi

AutoResetEvent Set yöntemi, çalışmayı sürdürmek için sinyali bekleyen iş parçacığına gönderdi. Aşağıda, Set yönteminin çağrılması sözdizimi verilmiştir.

autoResetEvent.Set();

ManualResetEvent , bellekte bir boole değişkenini korur. Boolean değişkeni false olduğunda, tüm iş parçacıklarını engeller ve boolean değişkeni true olduğunda tüm iş parçacıklarının engellemesini kaldırır.

Bir ManualResetEvent örneğini başlattığımızda, bunu varsayılan boole değeriyle başlatırız.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

Yukarıdaki kodda, ManualResetEvent öğesini false değeriyle başlatırız, yani WaitOne yöntemini çağıran tüm evreler, bazı evreler Set () yöntemini çağırana kadar engellenir.

ManualResetEvent öğesini true değeriyle başlatırsak, WaitOne yöntemini çağıran tüm evreler engellenmez ve ilerlemeye devam etmez.

WaitOne Yöntemi

Bu yöntem mevcut iş parçacığını engeller ve başka bir iş parçacığının sinyalini bekler. Başka bir sinyal alırsa true değerini döndürür.

Aşağıda WaitOne yönteminin çağrılması sözdizimi verilmiştir.

manualResetEvent.WaitOne();

WaitOne yönteminin ikinci aşırı yüklenmesinde, geçerli iş parçacığının sinyali beklemesine kadar geçen zaman aralığını belirleyebiliriz. Dahili süre içinde, yanlış döndürdüğü ve bir sonraki yöntem satırına girdiği bir sinyal almazsa.

Aşağıda WaitOne yönteminin zaman aralığı ile çağrılması sözdizimi verilmiştir.

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

WaitOne yöntemine 5 saniye belirledik. ManualResetEvent nesnesi 5 saniye arasında bir sinyal almazsa, isSignalled değişkenini false olarak ayarlar.

Set Yöntemi

Bu yöntem, sinyali tüm bekleyen iş parçacıklarına göndermek için kullanılır. Set () Yöntemi, ManualResetEvent nesnesi boolean değişkenini true olarak ayarlar. Tüm bekleyen evrelerin engelleri kaldırılır ve daha fazla ilerler.

Aşağıda, Set () yönteminin çağrılması sözdizimi verilmiştir.

manualResetEvent.Set();

Sıfırlama Yöntemi

ManualResetEvent nesnesindeki Set () yöntemini çağırdığımızda, boole değeri true olarak kalır. Değeri sıfırlamak için Reset () yöntemini kullanabiliriz. Sıfırlama yöntemi, boolean değerini false olarak değiştirir.

Sıfırlama yönteminin çağrılması sözdizimi aşağıdadır.

manualResetEvent.Reset();

İş parçacıklarına birden çok kez sinyal göndermek istiyorsak Set yöntemini çağırdıktan sonra hemen Reset yöntemini çağırmalıyız.


7

Evet. Bu kesinlikle doğru.

Durumu belirtmenin bir yolu olarak ManualResetEvent öğesini görebilirsiniz. Bir şey açık (Ayarla) veya kapalı (Sıfırla). Bir süreli bir olay. Bu durumun gerçekleşmesini bekleyen tüm evreler devam edebilir.

AutoResetEvent, bir sinyalle daha karşılaştırılabilir. Bir şey olduğunu gösteren tek atış. Süresiz bir olay. Genellikle ancak zorunlu olarak gerçekleşmeyen "bir şey" küçüktür ve tek bir iş parçacığı tarafından ele alınması gerekir - dolayısıyla tek bir iş parçacığı olayı tükettikten sonra otomatik sıfırlama.


7

Evet bu doğru.

Bu ikisini kullanarak bir fikir edinebilirsiniz.

Bazı işlerin bittiğini söylemeniz gerekiyorsa ve bunun için bekleyen diğer (iş parçacıkları) artık devam edebilir, ManualResetEvent'i kullanmalısınız.

Herhangi bir kaynağa karşılıklı özel erişiminiz olması gerekiyorsa, AutoResetEvent'i kullanmalısınız.


1

AutoResetEvent ve ManualResetEvent'i anlamak istiyorsanız, iş parçacığı değil kesmeleri anlamanız gerekir!

.NET, mümkün olan en uzaktaki düşük seviyeli programlamayı ortaya çıkarmak istiyor.

Bir kesinti, düşük seviyeli programlamada kullanılan ve düşükten yüksek (veya viceversa) bir sinyale eşit olan bir şeydir. Bu durumda, program normal yürütmesini yarıda keser ve yürütme işaretçisini bu olayı işleyen işleve taşır .

Bir kesme olayı olduğunda yapılacak ilk şey , donanımın bu şekilde çalışması nedeniyle durumunu sıfırlamaktır :

  1. bir sinyale bir pim bağlanır ve donanım değiştirilmesini dinler (sinyalin sadece iki durumu olabilir).
  2. sinyal değişirse, bir şey olduğu anlamına gelir ve donanım duruma bir bellek değişkeni koyar (ve sinyal tekrar değişse bile bu şekilde kalır).
  3. program, değişkenin değiştiğini ve yürütmeyi bir işleme işlevine taşıdığını fark eder.
  4. burada yapılacak ilk şey, bu kesmeyi tekrar dinleyebilmek için, bu bellek değişkenini gerçekleşmemiş duruma sıfırlamaktır .

Bu, ManualResetEvent ve AutoResetEvent arasındaki farktır.
Eğer bir ManualResetEvent gerçekleşirse ve sıfırlamazsam, bir dahaki sefer gerçekleştiğinde dinleyemeyeceğim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.