Чи події C # синхронні?


104

У цьому питанні є дві частини:

  1. Чи має підвищення події блокувати потік, або він починається виконання EventHandlers асинхронно і йде потік триває в той же час?

  2. Чи проводяться окремі EventHandlers (підписані на подію) синхронно один за одним, або вони працюють асинхронно, не гарантуючи, що інші не працюють одночасно?

Відповіді:


37

Щоб відповісти на ваші запитання:

  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:

введіть тут опис зображення

Зауважте, що є поле 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екземплярі.


3
Молодці !! Пройшло так давно, як я задав це питання. Якщо ви можете поставити верхівку вгорі, яка безпосередньо відповідає на моє питання з двох частин (тобто "відповідь №1 - ні; відповідь №2 - ні"), тоді я зроблю це офіційною відповіддю. Я ставлю на ваш пост як на всі частини, щоб відповісти на мої оригінальні запитання, але оскільки я більше не використовую C # (а інші googlers можуть бути новими в цих поняттях), тому я прошу багатослівність, яка робить відповіді очевидними.
Олександр Птах

Дякую @AlexanderBird, щойно відредагував це, щоб поставити відповіді вгорі.
KFL

@KFL, досі незрозуміло, я збирався залишити той самий коментар, що й Алекс. Просте "Так, вони синхронні", було б корисно
johnny 5

71

Це загальна відповідь і відображає поведінку за замовчуванням:

  1. Так, він блокує потік, якщо методи, підписані на подію, не є асинхронними.
  2. Вони виконуються один за одним. У цьому є ще один поворот: якщо один обробник подій кидає виняток, обробники подій ще не виконані не будуть виконані.

Сказавши це, кожен клас, який надає події, може вибрати асинхронну реалізацію своєї події. IDesign забезпечує клас, який називається, EventsHelperщо спрощує це.

[Примітка] за цим посиланням потрібно вказати адресу електронної пошти для завантаження EventHelper класу. (Я жодним чином не пов'язаний)


Я прочитав кілька дописів на форумі, з яких два суперечили першому моменту, не надаючи належної причини. Я не сумніваюся у вашій відповіді, (це відповідає тому, що я переживав досі) чи є офіційна документація на перший пункт? Мені потрібно бути певним щодо цього, але у мене є труднощі знайти щось офіційне з цього питання.
Адам ЛС

@ AdamL.S. Це питання, як називається подія. Тож це дійсно залежить від класу, що забезпечує подію.
Даніель Гілгарт

14

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

Оскільки події визначені делегатами з декількох повідомлень, ви можете написати власний механізм стрільби, використовуючи

Delegate.GetInvocationList();

асинхронно викликати делегатів;




3

Події в C # запускаються синхронно (в обох випадках), якщо ви не запустите другий потік вручну.


Що про те, що я використовую обробник подій async? Чи буде він потім працювати в іншій нитці? Я чув про "Async весь шлях", але, схоже, обробники подій async мають власну нитку? Я не розумію: / Чи можете ви мене просвітити?
Вінгер Сендон

3

Події синхронні. Ось чому життєвий цикл події працює так, як це відбувається. Ініти трапляються до навантаження, вантажі трапляються до нанесення тощо.

Якщо для події не вказано жодного обробника, цикл просто прогортає. Якщо вказано більше одного обробника, вони будуть викликані по порядку, і один не може продовжувати, поки інший не буде повністю закінчений.

Навіть асинхронні дзвінки є синхронними до певної міри. Завершити це було б неможливо до завершення початку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.