Цитата(neutrino @ 29.6.2008, 11:22 ) | | случился context switch между проверкой и вызовом someEvent, и тот поток, который получил процессорное время отписал обработчик события |
Читаем http://guerillatactics.co.uk/blog/archive/2008/03/23/alternative-way-to-raise-events.aspx :
| Код | if (someEvent != null) { someEvent(this, someEventArgs); }
|
| Цитата | This is partially right and in a single threaded environment will work fine but this code is not actually thread safe. If you imagine that the event is basically just a list of delegates and if you consider that your application may be using multiple threads then it is possible that between you performing the check for null and actually invoking the event the list may have changed. For example there may have been one delegate in there at the time of the check which is not there when the invoke occurs causing an exception to be thrown.
|
| Цитата | Для некоторых ситуаций это подходит : в однопоточном приложении этот код будет отлично работать, однако он не является tread safe. Если представить, что событие - это всего лишь список делегатов и ваше приложение может использовать несколько потоков, то возможно, что между проверкой на null и самим возникновением события, список делегатов может измениться. Например, на момент проверки имеется зарегестрированный делегат, а на момент вызова - он уже снят, что в результате приведёт к возникновению исключения.
|
Не вижу ничего необычного в такой ситуации, это возможно вообще для любой переменной, не только для события.
| Код | EventHandler temp = someEvent;
if (temp != null) { temp(this, someEventArgs); }
|
Цитата(neutrino @ 29.6.2008, 11:22 ) | | Майкрософт (чтоб она сдохла) нашла "решение" для этой ситуации и даже вписала для него сниппет "invoke": |
Тут Вы горячитесь. Что имеется в виду :
| Цитата | A thread safe way to invoke events is to make a copy of the delegate list and use that. In the past I've always done this anyway even if I'm in a single threaded app partially out of habit and partially because it means I can potentially introduce threading later without having to worry about my events.
|
| Цитата | Поточно-безопасным способом вызова событий может быть создание копии списка делегатов. Раньше я делал это всякий раз, даже в однопточном приложении, не совсем уж без пользы, поскольку в дальнейшем я бы мог мог перейти к многопоточности, не беспокоясь о событиях.
|
То есть смысл копирования в потокобезопасности, а именно :
| Код | EventHandler<EventArgs> temp = someEvent; if(temp != null) { // например, тут влезает другой поток: someEvent = null; temp(this, EventArgs.Empty); // ? Почему вызывается SimpleCallback(), а не throw NullRefferenceException ? }
|
| Цитата | В куче лежит объект типа EventHandler<EventArgs> назовем его A. someEvent указывает на A. После temp = someEvent, temp указывает на А, а не на someEvent.
someEvent = null; указывает на null, а temp все также на А.
|
То есть, если someEvent изменится, а А - нет, то temp не изменится.
Цитата http://www.gotdotnet.ru/Forums/Common/529713.aspx.
|