2014年08月20日

C#で出力の一時停止と再開

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その34

今回の投稿が、1111回目となります \(^o^)/

■問題
起動すると、標準出力に1秒毎に'a'の1文字を出力し続けるプログラムで、 以下の条件を満たすものを「どう書く?」
'q'キーが押されるとプログラムは終了する
出力中に'p'キーが押されると一時停止する
一時停止中に'p'キーが押されると出力を再開する 
         ※ すみなせん、出題者不明です。

3つの方法(Timer, Thread, Task)で書いています。
BackgroundWorkerで使うって方法もあるけど、それほど代わり映えしないので書いてません。
まずは、Timerを使ったコード
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            Execute();
            Console.WriteLine("Fin");
        }

        // System.Timers.Timerを使ったコード
        private static void Execute() {
            using (var timer = new System.Timers.Timer()) {
                timer.Interval = 1000;
                timer.Elapsed += (s, e) => Console.Write('a');
                timer.Enabled = true;
                while (true) {
                    var key = Console.ReadKey(false);
                    if (key.KeyChar == 'q')
                        break;
                    if (key.KeyChar == 'p') {
                        timer.Enabled = !timer.Enabled;
                    }
                }
                timer.Enabled = false;
            }
        }
    }
}
次はThreadを使ったコード
private static void Execute() {
    ManualResetEvent mre = new ManualResetEvent(true);
    Thread thread = new Thread(
        () => {
            while (true) {
                Console.Write('a');
                Thread.Sleep(1000);
                mre.WaitOne();
            }
        }
    );
    thread.Start();
    bool suspend = false;
    while (true) {
        var key = Console.ReadKey(false);
        if (key.KeyChar == 'q') {
            mre.Set();
            break;
        }
        if (key.KeyChar == 'p') {
            if (suspend)
                mre.Set();
            else
                mre.Reset();
            suspend = !suspend;
        }
    }
    thread.Abort();
}

最後は、Taskを使った例。
なんだか、だんだん複雑になっていくなー。 前のコードと同じようにManualResetEventを使って、一時停止を実現しているけど、他によい方法はないのかしら?
Taskクラスは複雑すぎて僕には使いこなせません。Threadのほうがはるかに単純で扱いやすいと思うのは僕だけでしょうか?

private static void Execute() {
    CancellationTokenSource tokenSource = new CancellationTokenSource();
    ManualResetEvent mre = new ManualResetEvent(true);   
    var task = new Task(
        () => {
            while (!tokenSource.IsCancellationRequested) {
                Console.Write('a');
                Thread.Sleep(1000);
                mre.WaitOne();
            }
        },
        tokenSource.Token
    );
    try {
        task.Start();
        bool suspend = false;
        while (true) {
            var key = Console.ReadKey(false);
            if (key.KeyChar == 'q') {
                mre.Set();
                break;
            }
            if (key.KeyChar == 'p') {
                if (suspend)
                    mre.Set();
                else
                    mre.Reset();
                suspend = !suspend;
            }
        }
        tokenSource.Cancel();
        task.Wait();
    } finally {
        task.Dispose();
    }
}


 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52380670