.NETコンソールアプリを実行し続ける方法は?


104

別のスレッドでいくつかのサービスを起動するコンソールアプリケーションについて考えてみます。ユーザーがCtrl + Cを押してシャットダウンするのを待つだけです。

次のうちどれがこれを行うためのより良い方法ですか?

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
}

または、これはThread.Sleep(1)を使用します。

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
}

回答:


63

特にコードで変数を再チェックする場合は、whileループの使用を常に回避する必要があります。CPUリソースを浪費し、プログラムの速度を低下させます。

私は間違いなく最初のものを言います。


2
+1。また、boolがとして宣言されていないvolatileため_quitFlagwhileループ内の後続の読み取りが最適化されて無限ループにつながる可能性があります。
Adam Robinson

2
これを行うための推奨方法がありません。これは答えだと思っていました。
Iúriドスアンジョス

30

または、より簡単な解決策は次のとおりです。

Console.ReadLine();

私はそれを提案しようとしていましたが、それはCtrl-Cだけで止まるわけではありません
Thomas Levesque

CTRL-Cはほんの一例-それを閉じるためのユーザー入力
Cocowalla

'Console.ReadLine()'はスレッドブロックであることを忘れないでください。したがって、アプリケーションは引き続き実行されますが、ユーザーが列に入るのを待つ以外は何もしません
fabriciorissetto

2
@fabriciorissetto OPの質問には「キックオフ非同期のもの」と記載されているため、アプリケーションは別のスレッドで作業を実行します
Cocowalla

1
@Cocowalla私はそれを逃した。悪い!
Fabriciorissetto、2015年

12

あなたはそれを行うことができます(そしてCancelKeyPressイベントハンドラを削除します):

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

それが良いかどうかはわかりませんThread.Sleepが、ループで呼び出すという考えは好きではありません。ユーザー入力をブロックする方がクリーンだと思います。


Ctrl + Cによってトリガーされる信号の代わりに、Ctrl + Cのキーをチェックしているのが好きではありません。
CodesInChaos 2015年

9

Application.Runを使用する

static void Main(string[] args) {

   //Do your stuff here

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

   //Cleanup/Before Quit
}

ドキュメントから:

フォームなしで、現在のスレッドで標準アプリケーションメッセージループの実行を開始します。


9
しかし、そのためにWindowsフォームに依存することになります。従来の.NETフレームワークの問題はそれほど多くありませんが、現在の傾向は、必要な部分だけを含むモジュール式の展開です。
CodesInChaos 2015年

4

必要以上に難しくしているようです。Join停止するようにシグナルを送った後のスレッドだけではないのですか?

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
私の場合、非同期のものを実行するスレッドを制御しません。
intoOrbit 2010

1)Ctrl + Cによってトリガーされる信号の代わりに、Ctrl + Cのキーをチェックするのが好きではありません。2)アプリケーションが単一のワーカースレッドではなくタスクを使用している場合、このアプローチは機能しません。
CodesInChaos 2015年

2

キャンセルトークンに基づいてスレッド/プログラムをブロックすることもできます。

token.WaitHandle.WaitOne();

トークンがキャンセルされると、WaitHandleが通知されます。

Microsoft.Azure.WebJobs.JobHostでこの手法が使用されているのを見てきました。トークンはWebJobsShutdownWatcher(ジョブを終了するファイルウォッチャー)のキャンセルトークンソースから取得されます。

これにより、プログラムをいつ終了できるかをある程度制御できます。


1
これは、CTL+C実行時間の長い操作を実行している、またはデーモンであり、ワーカースレッドを適切にシャットダウンする必要があるため、リッスンする必要がある実際のコンソールアプリにとって優れた回答です。あなたはそれをCancelTokenで行うので、この答えはWaitHandle新しいものを作成するのではなく、すでに存在しているであろうものを利用します。
mdisibio

1

2つのうち最初のものはより良いです

_quitEvent.WaitOne();

2番目のスレッドは1ミリ秒ごとにスレッドが起動するため、高価なOS割り込みが発生します。


これは、のために良い代替であるConsoleあなたが接続したコンソールを持っていない場合(例えば、プログラムがサービスによって開始された、ので)メソッド
マルコ・スッラ

0

Windowsサービスをプログラミングする場合と同じように行う必要があります。デリゲートを使用する代わりに、whileステートメントを使用することはありません。WaitOne()は通常、スレッドが破棄されるのを待つ間に使用されます-Thread.Sleep()-表示されません-そのイベントを使用してSystem.Timers.Timerを使用してシャットダウンイベントをチェックすることを考えましたか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.