いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

キー入力を受け付けながら処理を行う. (Console.Read, Console.ReadKey, ThreadPool, QueueUserWorkItem, IAsyncResult, Func, BeginInvoke, EndInvoke)


たまたま知人より、キー入力を受け付けながら処理を行う事って出来るの??って聞かれたので
書いたサンプルです。もったいないので、メモメモ。


Console.ReadやConsole.ReadKeyメソッドは処理をブロックするので、そのまま処理のループの中で
呼んでしまうと制御が返ってこなくなります。


なので、キー入力を受け付ける処理を別のスレッドに分けて、メインとサブで
処理をするとすっきりします。


以下、殴り書きのサンプルです。
読みづらいです。すみません。

using System;
using System.Threading;

namespace Test {

    public class Sample {

        volatile static bool _keyReaded = false;

        static void Main() {

            ThreadPool.QueueUserWorkItem(new WaitCallback(MyHandler), null);

            for(;;) {
                if(_keyReaded) {
                    Console.WriteLine("キーが押されました");
                    break;
                }

                Console.Write("Sleep in....");
                Thread.Sleep(1000);
                Console.WriteLine("end.");
            }
        }

        static void MyHandler(object userState) {
            ConsoleKeyInfo keyInfo = Console.ReadKey(true);
            _keyReaded = true;
        }
    }
}


当然、複数のスレッドが参照する_keyReaded変数は、volatileするかlockする必要があります。


補足)ちなみに、非同期デリゲートでも同じように出来ますね。

using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Test {
    
    delegate ConsoleKeyInfo AsyncReadKey(bool displayChars);

    public class Sample {

        volatile static bool _keyReaded = false;

        static void Main() {

            new AsyncReadKey(Console.ReadKey).BeginInvoke(true, MyHandler, null);

            for(;;) {
                if(_keyReaded) {
                    Console.WriteLine("キーが押されました");
                    break;
                }

                Console.Write("SLEEP IN....");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine("END");
            }
        }

        static void MyHandler(IAsyncResult ar) {

            AsyncResult  asyncResult   = (AsyncResult)  ar;
            AsyncReadKey asyncDelegate = (AsyncReadKey) asyncResult.AsyncDelegate;

            ConsoleKeyInfo keyInfo = asyncDelegate.EndInvoke(asyncResult);
            _keyReaded = true;
        }

    }
}


さらについでに、自前でデリゲートを切らずにフレームワーク付属の汎用デリゲートを利用した版

using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Test {
    
    public class Sample {

        volatile static bool _keyReaded = false;

        static void Main() {

            new Func<bool, ConsoleKeyInfo>(Console.ReadKey).BeginInvoke(true, MyHandler, null);

            for(;;) {
                if(_keyReaded) {
                    Console.WriteLine("キーが押されました");
                    break;
                }

                Console.Write("SLEEP IN....");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine("END");
            }
        }

        static void MyHandler(IAsyncResult ar) {

            AsyncResult                asyncResult   = (AsyncResult)  ar;
            Func<bool, ConsoleKeyInfo> asyncDelegate = (Func<bool, ConsoleKeyInfo>) asyncResult.AsyncDelegate;

            ConsoleKeyInfo keyInfo = asyncDelegate.EndInvoke(asyncResult);
            _keyReaded = true;
        }

    }
}

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。