с событий# синхронно?


есть две части этого вопроса:

  1. тут повышение событие блокирует поток, или он начинает выполнение EventHandlers асинхронно, и поток продолжается в то же время?

  2. Это отдельные обработчики событий (подписано на событие) запускаются синхронно один за другим, или они запускаются асинхронно без гарантии того, что другие не работают одновременно время?

7 82

7 ответов:

чтобы ответить на ваши вопросы:

  1. создание события блокирует поток, если все обработчики событий реализованы синхронно.
  2. обработчики событий выполняются последовательно, один за другим, в том порядке, в котором они подписаны на событие.

мне тоже было любопытно о внутреннем механизме event и связанные с ними операции. Поэтому я написал простую программу и использовать ildasm чтобы покопаться в его реализации.

короткий ответ:

  • нет никакой асинхронной операции, связанной с подпиской или вызовом событий.
  • событие реализуется с помощью резервного поля делегата того же типа делегата
  • подписка осуществляется с Delegate.Combine()
  • отписка производится с помощью Delegate.Remove()
  • вызов выполняется путем простого вызова окончательного комбинированного делегат

вот что я сделал. Программа, которую я использовал:

public class Foo
{
    // cool, it can return a value! which value it returns if there're multiple 
    // subscribers? answer (by trying): the last subscriber.
    public event Func<int, string> OnCall;
    private int val = 1;

    public void Do()
    {
        if (OnCall != null) 
        {
            var res = OnCall(val++);
            Console.WriteLine($"publisher got back a {res}");
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub2: I've got a {i}");
            return "sub2";
        };

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub1: I've got a {i}");
            return "sub1";
        };

        foo.Do();
        foo.Do();
    }
}

вот реализация Foo:

enter image description here

обратите внимание, что есть полеOnCall и событиеOnCall. Поле OnCall это, очевидно, свойство поддержки. И это просто Func<int, string>, ничего особенного здесь.

теперь интересные части являются:

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • и как OnCall вызывается в Do()

как осуществляется подписка и отмена подписки?

вот сокращенно add_OnCall реализация в CIL. Самое интересное, что он использует Delegate.Combine объединить двух делегатов.

.method public hidebysig specialname instance void 
        add_OnCall(class [mscorlib]System.Func`2<int32,string> 'value') cil managed
{
  // ...
  .locals init (class [mscorlib]System.Func`2<int32,string> V_0,
           class [mscorlib]System.Func`2<int32,string> V_1,
           class [mscorlib]System.Func`2<int32,string> V_2)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall
  // ...
  IL_000b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  // ...
} // end of method Foo::add_OnCall

кроме того, Delegate.Remove используется remove_OnCall.

как это событие вызывали?

для вызова OnCall на Do(), он просто вызывает окончательный объединенный делегат после загрузки arg:

IL_0026:  callvirt   instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)

как именно подписчик подписывается на событие?

и, наконец, в Main, не удивительно, подписавшись на OnCall событие выполняется путем вызова add_OnCall метод Foo экземпляра.

Это общий ответ и отражает поведение по умолчанию:

  1. Да, он блокирует поток, если методы подписки на событие не асинхронный.
  2. они выполняются один за другим. Это имеет еще один поворот: если один обработчик событий создает исключение, обработчики событий, которые еще не выполнены, не будут выполнены.

сказав, что каждый класс, который предоставляет события может реализовать свое событие асинхронно. IDesign предоставляет класс под названием EventsHelper это упрощает дело.

[Примечание] по этой ссылке необходимо указать адрес электронной почты для загрузки класса EventsHelper. (Я никоим образом не связан)

делегаты, подписанные на событие, вызываются синхронно в том порядке, в котором они были добавлены. Если один из делегатов создает исключение, следующие не будет называться.

поскольку события определяются с помощью делегатов многоадресной рассылки, вы можете написать свой собственный механизм запуска с помощью

Delegate.GetInvocationList();

и вызов делегата асинхронно;

события - это просто массивы делегатов. Пока вызов делегата происходит синхронно, также синхронно.

В общем, события синхронно. Однако есть некоторые исключения, такие как System.Timers.Timer.Elapsed событие на ThreadPool thread if SyncronisingObject имеет значение null.

Docs:http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx

события в C# запускаются синхронно (в обоих случаях), если вы не запускаете второй поток вручную.

события одновременны. Вот почему жизненный цикл события работает именно так. Inits происходит до нагрузок, нагрузки происходят до рендеринга и т. д.

Если для события не указан обработчик, цикл просто проносится. Если указано более одного обработчика, они будут вызваны по порядку, и один не может продолжаться до тех пор, пока другой не будет полностью завершен.

даже асинхронные вызовы синхронны до некоторой степени. Было бы невозможно назвать конец до начала есть завершенный.