C#の非同期処理について考えてみた

どうも、お久しぶりです。開発が始まると脳のリソース全部そっちに持っていかれて記事で共有しようという考えがなかなか持てないものですね。
1週間ほどC#に挑戦することになりましたので、趣味の範囲で気になったことをまとめて残していこうと思います。


# 簡単なソケット通信の例
サーバー側
```
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Program
{
    static void Main()
    {
        // ローカルエンドポイントの作成
        IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        // ソケットの作成
        Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            // ソケットをローカルエンドポイントにバインド
            listener.Bind(localEndPoint);

            // 接続待ち状態に設定
            listener.Listen(10);

            Console.WriteLine("Waiting for a connection...");

            // 接続を受け入れ
            Socket handler = listener.Accept();

            // データの受信
            string data = null;
            byte[] bytes = new byte[1024];
            int bytesRec = handler.Receive(bytes);
            data += Encoding.ASCII.GetString(bytes, 0, bytesRec);

            Console.WriteLine("Text received : {0}", data);

            // データの送信
            byte[] msg = Encoding.ASCII.GetBytes("Message received");
            handler.Send(msg);

            // 接続の終了
            handler.Shutdown(SocketShutdown.Both);
            handler.Close();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

```
クライアント側
```
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Program
{
    static void Main()
    {
        // サーバーエンドポイントの作成
        IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, 11000);

        // ソケットの作成
        Socket sender = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            // サーバーに接続
            sender.Connect(remoteEP);

            Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());

            // データの送信
            byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
            int bytesSent = sender.Send(msg);

            // データの受信
            byte[] bytes = new byte[1024];
            int bytesRec = sender.Receive(bytes);
            Console.WriteLine("Echoed test = {0}", Encoding.ASCII.GetString(bytes, 0, bytesRec));

            // 接続の終了
            sender.Shutdown(SocketShutdown.Both);
            sender.Close();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

```
# 本題、これと、入力を一緒にやりたい。
シンプルに、Console.ReadLine();と組み合わせてみると、入力が返ってくるまでプログラムはフリーズしてしまう。
# Taskとasync/awaitを使って非同期に実行する方法
```
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static string name;
    private static AutoResetEvent nameReady = new AutoResetEvent(false);

    static async Task Main()
    {
        // ソケット通信を非同期に開始
        Task.Run(() => StartServer());

        while (true)
        {
            Console.WriteLine("Enter your name:");
            name = Console.ReadLine();
            Console.WriteLine("Hello, " + name + "!");

            // 名前が準備できたことを通知
            nameReady.Set();
        }
    }

    static void StartServer()
    {
        IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            listener.Bind(localEndPoint);
            listener.Listen(10);

            Console.WriteLine("Waiting for a connection...");

            while (true)
            {
                Socket handler = listener.Accept();

                Task.Run(() => HandleClient(handler));
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    static void HandleClient(Socket handler)
    {
        string data = null;
        byte[] bytes = new byte[1024];

        try
        {
            int bytesRec = handler.Receive(bytes);
            data += Encoding.ASCII.GetString(bytes, 0, bytesRec);

            Console.WriteLine("Text received : {0}", data);

            // 名前が準備できるまで待機
            nameReady.WaitOne();

            // 名前をクライアントに送信
            byte[] msg = Encoding.ASCII.GetBytes(name);
            handler.Send(msg);

            handler.Shutdown(SocketShutdown.Both);
            handler.Close();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

```
ヒントBeginInvokeによる非同期処理
```
using System;
using System.Threading;
using System.Windows.Forms;

namespace BeginInvokeExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // 非同期にバックグラウンド処理を開始
            Thread backgroundThread = new Thread(new ThreadStart(BackgroundProcess));
            backgroundThread.Start();
        }

        private void BackgroundProcess()
        {
            // 5秒間の遅延をシミュレート
            Thread.Sleep(5000);

            // UIスレッドでテキストを更新（非同期的に実行）
            BeginInvoke((Action)(() =>
            {
                textBox1.Text = "Updated by BeginInvoke";
            }));
        }
    }
}

```