2014年10月19日
C#でキッチンタイマー
「どう書く?orgに感謝を込めて」シリーズ その43
一ヶ月ほど空いてしまいましたが、このシリーズ再開します。
WPFの記事に比べると、本当にアクセスが少ないシリーズですが、気にせず進めていきます。(^^;;
■問題 (ところてん さん)
まずは、KitchenTimer の定義。
一定間隔毎に引数で受け取ったActionデリゲートがよばれるようにしています。
また、タイマーが終了した時点でも、もう一つのActionデリゲートがよばれます。
こうすることで、前回のバイナリークロック同様、GUIへの依存がないようになっています。
このクラスを使い WindowsFormsアプリを作成します。
タイマーが切れたらブザーが鳴りますが、このブザーは、Stopボタンが押されるまで鳴り続けます。
こまかな説明は省略。
一ヶ月ほど空いてしまいましたが、このシリーズ再開します。
WPFの記事に比べると、本当にアクセスが少ないシリーズですが、気にせず進めていきます。(^^;;
■問題 (ところてん さん)
キッチンタイマーを作ってください。 要件は以下のとおりです。・タイマーが鳴るまでの時間を入力可能 ・残り時間を表示 ・タイマーが切れたら音がなる
まずは、KitchenTimer の定義。
using System; using System.Timers; namespace Doukaku.Org { public class KitchenTimer { private System.Timers.Timer _timer; private TimeSpan _elapsedTime; private TimeSpan _target; private Action _action; private Action _endAction; private Object _lockobj = new object(); public void Start(TimeSpan time, Action action, Action endAction) { _elapsedTime = new TimeSpan(); _target = time; _action = action; _endAction = endAction; _timer = new System.Timers.Timer(); _timer.Interval = 1000; _timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); _timer.Start(); } void timer_Elapsed(object sender, ElapsedEventArgs e) { lock (_lockobj) { if (_timer == null) return; _elapsedTime = _elapsedTime.Add(new TimeSpan(0, 0, 1)); _action(); if (_target <= _elapsedTime) { _timer.Stop(); _endAction(); } } } public void Stop() { lock (_lockobj) { _timer.Stop(); _timer = null; }; } public TimeSpan TimeLeft { get { var left = _target - _elapsedTime; if (left.TotalMilliseconds <= 0) return new TimeSpan(0, 0, 0); return left; } } } }
一定間隔毎に引数で受け取ったActionデリゲートがよばれるようにしています。
また、タイマーが終了した時点でも、もう一つのActionデリゲートがよばれます。
こうすることで、前回のバイナリークロック同様、GUIへの依存がないようになっています。
このクラスを使い WindowsFormsアプリを作成します。
タイマーが切れたらブザーが鳴りますが、このブザーは、Stopボタンが押されるまで鳴り続けます。
こまかな説明は省略。
using System; using System.Media; using System.Threading; using System.Windows.Forms; namespace Doukaku.Org { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private Status status = Status.Stop; private int minutes = 0; private int seconds = 0; private KitchenTimer kt; private void buttonStart_Click(object sender, EventArgs e) { switch (status) { case Status.Stop: Start(); break; case Status.Running: Stop(); break; case Status.Buzzer: Restore(); break; } } private void buttonReset_Click(object sender, EventArgs e) { if (status == Status.Stop) { status = Status.Stop; buttonStart.Text = "Start"; numericUpDown1.Value = 0; numericUpDown2.Value = 0; } } private void Start() { minutes = (int)numericUpDown1.Value; seconds = (int)numericUpDown2.Value; TimeSpan ts = new TimeSpan(0, minutes, seconds); status = Status.Running; buttonStart.Text = "Stop"; kt = new KitchenTimer(); kt.Start(ts, () => { this.Invoke((MethodInvoker)delegate { TimeSpan left = kt.TimeLeft; numericUpDown1.Value = left.Minutes; numericUpDown2.Value = left.Seconds; }); }, () => { status = Status.Buzzer; while (status == Status.Buzzer) { SystemSounds.Asterisk.Play(); Thread.Sleep(200); } } ); } private void Stop() { kt.Stop(); Restore(); } private void Restore() { status = Status.Stop; buttonStart.Text = "Start"; numericUpDown1.Value = minutes; numericUpDown2.Value = seconds; } enum Status { Stop, Running, Buzzer, } } }
2014年08月20日
C#で出力の一時停止と再開
「どう書く?orgに感謝を込めて」シリーズ その34
今回の投稿が、1111回目となります \(^o^)/
■問題
3つの方法(Timer, Thread, Task)で書いています。
BackgroundWorkerで使うって方法もあるけど、それほど代わり映えしないので書いてません。
まずは、Timerを使ったコード
最後は、Taskを使った例。
なんだか、だんだん複雑になっていくなー。 前のコードと同じようにManualResetEventを使って、一時停止を実現しているけど、他によい方法はないのかしら?
Taskクラスは複雑すぎて僕には使いこなせません。Threadのほうがはるかに単純で扱いやすいと思うのは僕だけでしょうか?
今回の投稿が、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(); } }