.NET konsol uygulaması nasıl çalışır durumda tutulur?


105

Bazı hizmetleri ayrı bir iş parçacığında başlatan bir Konsol uygulamasını düşünün. Tek yapması gereken, kullanıcının kapatmak için Ctrl + C'ye basmasını beklemektir.

Bunu yapmanın daha iyi yolu aşağıdakilerden hangisidir?

static ManualResetEvent _quitEvent = new ManualResetEvent(false);

static void Main() {
    Console.CancelKeyPress += (sender, eArgs) => {
        _quitEvent.Set();
        eArgs.Cancel = true;
    };

    // kick off asynchronous stuff 

    _quitEvent.WaitOne();

    // cleanup/shutdown and quit
}

Veya bu, Thread.Sleep (1) kullanarak:

static bool _quitFlag = false;

static void Main() {
    Console.CancelKeyPress += delegate {
        _quitFlag = true;
    };

    // kick off asynchronous stuff 

    while (!_quitFlag) {
        Thread.Sleep(1);
    }

    // cleanup/shutdown and quit
}

Yanıtlar:


64

her zaman, özellikle kodu değişkenleri yeniden kontrol etmeye zorladığınızda, while döngülerini kullanmayı önlemek istersiniz. CPU kaynaklarını boşa harcar ve programınızı yavaşlatır.

Kesinlikle ilkini söyleyebilirim.


2
+1. Ek boololarak volatile, olarak bildirilmediğinden _quitFlag, whiledöngüdeki sonraki okumaların optimize edilerek sonsuz bir döngüye yol açabileceği kesin bir olasılık vardır .
Adam Robinson

2
Bunu yapmanın önerilen yolu eksik. Bunu bir cevap olarak bekliyordum.
Iúri dos Anjos

30

Alternatif olarak, daha basit bir çözüm şudur:

Console.ReadLine();

Bunu önermek üzereydim, ancak sadece Ctrl-C ile bitmeyecek
Thomas Levesque

CTRL-C'nin sadece bir örnek olduğu izlenimine
kapıldım

'Console.ReadLine ()' öğesinin iş parçacığı engelleyici olduğunu unutmayın. Yani uygulama hala çalışıyor, ancak kullanıcının bir satıra girmesini beklemekten başka bir şey yapmıyordu
fabriciorissetto

2
@fabriciorissetto OP sorusu 'eşzamansız şeyleri başlatmayı' belirtir, bu nedenle uygulama başka bir iş parçacığı üzerinde çalışma gerçekleştirecek
Cocowalla

1
@Cocowalla Bunu özledim. Benim hatam!
fabriciorissetto

13

Bunu yapabilir (ve CancelKeyPressolay işleyicisini kaldırabilirsiniz ):

while(!_quitFlag)
{
    var keyInfo = Console.ReadKey();
    _quitFlag = keyInfo.Key == ConsoleKey.C
             && keyInfo.Modifiers == ConsoleModifiers.Control;
}

Bunun daha iyi olup olmadığından emin değilim, ama Thread.Sleepbir döngü içinde arama fikrini sevmiyorum .. Bence kullanıcı girdisini engellemek daha temiz.


Ctrl + C ile tetiklenen sinyal yerine Ctrl + C tuşlarını kontrol etmenden hoşlanmıyorum.
CodesInChaos

9

Uygulamayı kullanmayı tercih ediyorum.

static void Main(string[] args) {

   //Do your stuff here

   System.Windows.Forms.Application.Run();

   //Cleanup/Before Quit
}

dokümanlardan:

Geçerli iş parçacığı üzerinde form olmadan standart bir uygulama ileti döngüsü çalıştırmaya başlar.


11
Ama sonra sadece bunun için Windows formlarına bağımlı olursunuz. Geleneksel .NET çerçevesiyle ilgili çok fazla sorun değil, ancak mevcut eğilim, yalnızca ihtiyacınız olan parçaları içeren modüler dağıtımlara yöneliktir.
CodesInChaos

4

Görünüşe göre işi gerekenden daha zorlaştırıyorsun. Neden Joindurmasını işaret ettikten sonra sadece ileti dizisi değil ?

class Program
{
    static void Main(string[] args)
    {
        Worker worker = new Worker();
        Thread t = new Thread(worker.DoWork);
        t.IsBackground = true;
        t.Start();

        while (true)
        {
            var keyInfo = Console.ReadKey();
            if (keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers == ConsoleModifiers.Control)
            {
                worker.KeepGoing = false;
                break;
            }
        }
        t.Join();
    }
}

class Worker
{
    public bool KeepGoing { get; set; }

    public Worker()
    {
        KeepGoing = true;
    }

    public void DoWork()
    {
        while (KeepGoing)
        {
            Console.WriteLine("Ding");
            Thread.Sleep(200);
        }
    }
}

2
Benim durumumda, eşzamansız şeylerin çalıştığı konuları kontrol etmiyorum.
Orbit

1) Ctrl + C ile tetiklenen sinyal yerine Ctrl + C tuşlarını kontrol etmeni sevmiyorum. 2) Uygulama tek bir çalışan iş parçacığı yerine Görevler kullanıyorsa yaklaşımınız işe yaramaz.
CodesInChaos

3

İş parçacığını / programı bir iptal belirtecine göre bloke etmek de mümkündür.

token.WaitHandle.WaitOne();

Token iptal edildiğinde WaitHandle sinyali verilir.

Microsoft.Azure.WebJobs.JobHost tarafından kullanılan bu tekniği gördüm, burada belirteç WebJobsShutdownWatcher'ın (işi sonlandıran bir dosya izleyicisi) iptal belirteci kaynağından geliyor.

Bu, programın ne zaman sona ereceği konusunda biraz kontrol sağlar.


1
Bu, CTL+Cuzun süredir devam eden bir işlem gerçekleştirdiği için dinlemesi gereken veya çalışan iş parçacıklarını da zarif bir şekilde kapatması gereken bir arka plan programı olan herhangi bir gerçek dünya Konsol uygulaması için mükemmel bir cevaptır . Bunu bir CancelToken ile yaparsınız ve bu nedenle bu cevap, WaitHandleyeni bir tane oluşturmak yerine zaten var olan bir cevaptan yararlanır .
mdisibio

1

İlk ikisinden daha iyi

_quitEvent.WaitOne();

çünkü ikincisinde, iş parçacığı her milisaniyede bir uyanır ve pahalı olan işletim sistemi kesintisine dönüşür


Bu, Consolebağlı bir konsolunuz yoksa yöntemler için iyi bir alternatiftir (çünkü, örneğin, program bir hizmet tarafından başlatılmıştır)
Marco Sulla

0

Bunu, bir Windows servisi programlıyorsanız yaptığınız gibi yapmalısınız. Asla bir while ifadesi kullanmazsınız, bunun yerine bir temsilci kullanırsınız. WaitOne () genellikle iş parçacıklarının atılmasını beklerken kullanılır - Thread.Sleep () - tavsiye edilmez - Kapatma olayını kontrol etmek için bu olayı kullanarak System.Timers.Timer kullanmayı düşündünüz mü?

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.