с событий# синхронно?
есть две части этого вопроса:
тут повышение событие блокирует поток, или он начинает выполнение EventHandlers асинхронно, и поток продолжается в то же время?
Это отдельные обработчики событий (подписано на событие) запускаются синхронно один за другим, или они запускаются асинхронно без гарантии того, что другие не работают одновременно время?
7 ответов:
чтобы ответить на ваши вопросы:
- создание события блокирует поток, если все обработчики событий реализованы синхронно.
- обработчики событий выполняются последовательно, один за другим, в том порядке, в котором они подписаны на событие.
мне тоже было любопытно о внутреннем механизме
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
экземпляра.
Это общий ответ и отражает поведение по умолчанию:
- Да, он блокирует поток, если методы подписки на событие не асинхронный.
- они выполняются один за другим. Это имеет еще один поворот: если один обработчик событий создает исключение, обработчики событий, которые еще не выполнены, не будут выполнены.
сказав, что каждый класс, который предоставляет события может реализовать свое событие асинхронно. IDesign предоставляет класс под названием
EventsHelper
это упрощает дело.[Примечание] по этой ссылке необходимо указать адрес электронной почты для загрузки класса EventsHelper. (Я никоим образом не связан)
делегаты, подписанные на событие, вызываются синхронно в том порядке, в котором они были добавлены. Если один из делегатов создает исключение, следующие не будет называться.
поскольку события определяются с помощью делегатов многоадресной рассылки, вы можете написать свой собственный механизм запуска с помощью
Delegate.GetInvocationList();
и вызов делегата асинхронно;
В общем, события синхронно. Однако есть некоторые исключения, такие как
System.Timers.Timer.Elapsed
событие наThreadPool
thread ifSyncronisingObject
имеет значение null.Docs:http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
события одновременны. Вот почему жизненный цикл события работает именно так. Inits происходит до нагрузок, нагрузки происходят до рендеринга и т. д.
Если для события не указан обработчик, цикл просто проносится. Если указано более одного обработчика, они будут вызваны по порядку, и один не может продолжаться до тех пор, пока другой не будет полностью завершен.
даже асинхронные вызовы синхронны до некоторой степени. Было бы невозможно назвать конец до начала есть завершенный.