Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вызов событий. Кто чем пользуется? 
:(
    Опции темы
neutrino
Дата 29.6.2008, 11:22 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Gothic soul
****


Профиль
Группа: Модератор
Сообщений: 3041
Регистрация: 25.3.2002
Где: Верхняя Галилея, Кармиэль

Репутация: 3
Всего: 62



Привет!

Вот, собственно, если написать код:
Код

public event EventHandler someEvent;

protected virtual onSomeEvent()
{
if (someEvent != null) someEvent(this, new EventArgs());
}

То если случился context switch между проверкой и вызовом someEvent, и тот поток, который получил процессорное время отписал обработчик события, то мы получим NullReferenceException, т.к. someEvent уже будет null.

Майкрософт (чтоб она сдохла) нашла "решение" для этой ситуации и даже вписала для него сниппет "invoke":

Код

protected virtual onSomeEvent()
{
EventHandler temp = someEvent;
if (temp != null)
{
temp(this, new EventArgs());
}
}

Гы smile Прикольно? То есть если даже обработчик и будет отписан, он все равно вызовется. Оригинально!


--------------------
The truth comes from within ...

Покойся с миром, Vit 
PM MAIL WWW ICQ Skype GTalk   Вверх
Bogdan1024
Дата 29.6.2008, 12:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1436
Регистрация: 28.9.2005
Где: Киев

Репутация: 5
Всего: 13



Цитата
То если случился context switch между проверкой и вызовом someEvent, и тот поток, который получил процессорное время отписал обработчик события, то мы получим NullReferenceException, т.к. someEvent уже будет null.
Круто. Никогда не думал что такое может случиться.

Добавлено через 10 минут и 50 секунд
Наверно, можно ещё lock'чить someEvent чтобы никто от него не мог отписываться.

Это сообщение отредактировал(а) Bogdan1024 - 29.6.2008, 12:33


--------------------
user posted image
PM MAIL   Вверх
Idsa
Дата 29.6.2008, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 2086
Регистрация: 5.12.2006
Где: Томск

Репутация: 14
Всего: 62



neutrino, а разве переменная temp не будет ссылаться на тот же экземпляр делегата? Если так, то временная переменная бессмысленна.


--------------------
Мой блог: alexidsa.blogspot.com
PM MAIL ICQ   Вверх
Bogdan1024
Дата 29.6.2008, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1436
Регистрация: 28.9.2005
Где: Киев

Репутация: 5
Всего: 13



Я проверял кодом: реально никто ни на кого ссылаться не будет.
Код

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        private static event EventHandler eh1;
        static void Main(string[] args)
        {
            eh1 += new EventHandler(Do);
            EventHandler eh2 = eh1;
            eh1 -= new EventHandler(Do);
            eh2.Invoke(null, null);
            System.Console.ReadKey(true);
        }

        private static void Do(object sender, EventArgs ea)
        {
            System.Console.WriteLine("Do");
        }
    }
}



Это сообщение отредактировал(а) Bogdan1024 - 29.6.2008, 13:46


--------------------
user posted image
PM MAIL   Вверх
Idsa
Дата 29.6.2008, 14:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 2086
Регистрация: 5.12.2006
Где: Томск

Репутация: 14
Всего: 62



neutrino, спасибо за интересную тему. +1

Bogdan1024, действительно, значения копируются не по ссылке, а по значению. Интересно, с чего бы это...


--------------------
Мой блог: alexidsa.blogspot.com
PM MAIL ICQ   Вверх
neutrino
Дата 29.6.2008, 15:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Gothic soul
****


Профиль
Группа: Модератор
Сообщений: 3041
Регистрация: 25.3.2002
Где: Верхняя Галилея, Кармиэль

Репутация: 3
Всего: 62



Цитата(Bogdan1024 @  29.6.2008,  11:32 Найти цитируемый пост)
Наверно, можно ещё lock'чить someEvent чтобы никто от него не мог отписываться.

Да. Можно. Но вот представь себе сколько событий бывает... Да и performance... Можно ведь делать что-то типа:

Код

        private readonly object syncronizer = new object();

        private MouseEventHandler m_beginDragging;

        public event MouseEventHandler BeginDragging
        {
            add
            {
                lock (syncronizer)
                    m_beginDragging += value;
            }
            remove
            {
                lock (syncronizer)
                    m_beginDragging -= value;
            }
        }

        protected virtual void OnBeginDragging(MouseEventArgs e)
        {
            lock (syncronizer)
            {
                if (m_beginDragging != null)
                    m_beginDragging(this, e);
            }
        }


Ну это так "навскидку". Сейчас будем искать неточности и баги...


--------------------
The truth comes from within ...

Покойся с миром, Vit 
PM MAIL WWW ICQ Skype GTalk   Вверх
marcusmae
Дата 29.6.2008, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 22
Всего: 39



(глюк)

Это сообщение отредактировал(а) marcusmae - 29.6.2008, 17:02


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
marcusmae
Дата 29.6.2008, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 22
Всего: 39



Цитата(neutrino @  29.6.2008,  11:22 Найти цитируемый пост)
случился context switch между проверкой и вызовом someEvent, и тот поток, который получил процессорное время отписал обработчик события


Читаем первый попавшийся в поиске блог :

Код

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 не изменится.

Цитата gotdotnet.ru.


Это сообщение отредактировал(а) marcusmae - 29.6.2008, 17:24


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
Idsa
Дата 2.7.2008, 16:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 2086
Регистрация: 5.12.2006
Где: Томск

Репутация: 14
Всего: 62



Цитата(marcusmae @  29.6.2008,  21:01 Найти цитируемый пост)

То есть, если someEvent изменится, а А - нет, то temp не изменится.

С Вашим примером все понятно: там другая ссылка присваивается. А вот с пониманием этого кода:
Цитата(Bogdan1024 @  29.6.2008,  17:45 Найти цитируемый пост)

            eh1 += new EventHandler(Do);
            EventHandler eh2 = eh1;
            eh1 -= new EventHandler(Do);
            eh2.Invoke(null, null);
            System.Console.ReadKey(true);

у меня возникли проблемы. Ведь здесь и eh1, и eh2 указывают на один и тот же объект в памяти. А ведут себя, как будто копировались по значению.

А вообще, правда, не проще ли было завернуть if с проверкой в lock?!


--------------------
Мой блог: alexidsa.blogspot.com
PM MAIL ICQ   Вверх
PashaPash
Дата 2.7.2008, 18:16 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1233
Регистрация: 3.1.2008

Репутация: 13
Всего: 49



Цитата(Idsa @  2.7.2008,  16:59 Найти цитируемый пост)
Ведь здесь и eh1, и eh2 указывают на один и тот же объект в памяти. А ведут себя, как будто копировались по значению.
Вообще-то операции += и -= создают новые объекты:
Код
private static void Main(string[] args)
{
    EventHandler eh2;
    eh1 = (EventHandler) Delegate.Combine(eh1, new EventHandler(Program.Do));
    eh2 = eh1;
    eh1 = (EventHandler) Delegate.Remove(eh1, new EventHandler(Program.Do));
    eh2(null, null);
    Console.ReadKey(1);
    return;
}
Для не-делегатов, кстати, +=/-= себя тоже так ведут.
Цитата(Idsa @  2.7.2008,  16:59 Найти цитируемый пост)
А вообще, правда, не проще ли было завернуть if с проверкой в lock?!
Создать ссылку в стеке намного проще и дешевле чем сделать lock.

Это сообщение отредактировал(а) PashaPash - 2.7.2008, 18:21


--------------------
PM MAIL WWW   Вверх
marcusmae
Дата 2.7.2008, 19:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


stravaganza
**


Профиль
Группа: Участник
Сообщений: 874
Регистрация: 26.3.2006

Репутация: 22
Всего: 39



Цитата(Idsa @  2.7.2008,  16:59 Найти цитируемый пост)
А вообще, правда, не проще ли было завернуть if с проверкой в lock?!


Цитата(PashaPash @  2.7.2008,  18:16 Найти цитируемый пост)
Создать ссылку в стеке намного проще и дешевле чем сделать lock.


Ну да. Конечно, широко употребимый snippet с if-ом просто не предназначен для решения специфических проблем многопоточности, чего нигде и не утверждается, а универсальное решение будет весьма ущербно в плане производительности.


--------------------
ἀπὸ μηχανῆς θεός
PM MAIL ICQ GTalk   Вверх
neutrino
Дата 3.7.2008, 08:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Gothic soul
****


Профиль
Группа: Модератор
Сообщений: 3041
Регистрация: 25.3.2002
Где: Верхняя Галилея, Кармиэль

Репутация: 3
Всего: 62



marcusmae, Да я этo все понимаю. Нo вот получается, чтo если обработчик события отписался (между if и вызовом) то он все равно вызовется.


--------------------
The truth comes from within ...

Покойся с миром, Vit 
PM MAIL WWW ICQ Skype GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.1196 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.