LoginSignup
1
3

More than 5 years have passed since last update.

EventHandlerのInvoke()が同期実行であることに今まで気付かなかったから実験した

Last updated at Posted at 2018-12-16

導入

ただのマヌケ話ですが、実験したコードと非同期で実行する方法についてのメモを記録しておきます。
もし同じ誤解をしている方がいたとして、役に立てれば幸いです。

実験

超シンプルな実験用コードです。ConsoleアプリケーションでEventHandlerを持つクラスのインスタンスを作り、意図的にイベントを起こします。
実行されるコードの周囲にConsole.WriteLine(string)をちりばめ、出力されるテキストから同期か非同期かを判断します。

Program.cs
using System;
using System.Threading;

namespace EventHandlerInvokeTest
{
    class Program
    {
        private static EventInvoker invoker;
        static void Main(string[] args)
        {
            invoker = new EventInvoker();
            invoker.TrialValueChanged += (sender, b) =>
            {
                Console.WriteLine("TrialValueChanged callback enter");
                Thread.Sleep(5*1000);
                Console.WriteLine("TrialValueChanged callback exit");
            };

            Console.WriteLine("Main sync start");
            invoker.TrialValueSync = true;
            Console.WriteLine("Main sync end");

            Console.WriteLine("Main async start");
            invoker.TrialValueAsync = true;
            Console.WriteLine("Main async end");

            Console.ReadKey();
        }
    }
}

イベントを実行するEventInvokerは掲題のInvoke()を実行するものと、恐らく非同期であろうBeginInvoke()を実行するプロパティを持ちます。

EventInvoker.cs
using System;

namespace EventHandlerInvokeTest
{
    public class EventInvoker
    {
        private bool _TrialValueSync = false;
        private bool _TrialValueAsync = false;

        public bool TrialValueSync
        {
            get
            {
                return _TrialValueSync;
            }
            set
            {
                Console.WriteLine("Set sync enter");
                if (_TrialValueSync != value)
                {
                    _TrialValueSync = value;
                    TrialValueChanged?.Invoke(this, _TrialValueSync);
                }
                Console.WriteLine("Set sync exit");
            }
        }

        public bool TrialValueAsync
        {
            get
            {
                return _TrialValueAsync;
            }
            set
            {
                Console.WriteLine("Set async enter");
                if (_TrialValueAsync != value)
                {
                    _TrialValueAsync = value;
                    TrialValueChanged?.BeginInvoke(this, _TrialValueAsync, null, null);
                }
                Console.WriteLine("Set async exit");
            }
        }

        public event EventHandler<bool> TrialValueChanged;
    }
}

実行結果

最初に実行されたInvoke()はコールバックの終了を待ってからプロパティのSetが終わっていますが、次に実行されたBeginInvoke()はコールバックが始まるより先にSetが終わっています。

Main sync start
Set sync enter
TrialValueChanged callback enter
TrialValueChanged callback exit
Set sync exit
Main sync end
Main async start
Set async enter
Set async exit
Main async end
TrialValueChanged callback enter
TrialValueChanged callback exit

非同期実行のBeginInvoke()について

実験では3番目と4番目にnullを突っ込んでいますが、まだここについてまとめてないので、でき次第アップデートします。

おわりに

この件についてアドバイスいただいた先輩に感謝します。
デリゲートを使用した非同期プログラミング

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3