Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, nikov, Вы писали:
N>>
N>>When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. In order to be thread-safe, the addition or removal operations are done while holding the lock (§8.12) on the containing object for an instance event, or the type object (§7.5.10.6) for a static event.
S>Прекольно. То есть они напрашиваются на дедлок? Что ж это они блокируются на объемлющем объекте. И как, кстати, они собрались блокироваться на структуре?
А никак — в ECMA-334 в пункте 17.7.1 Field-like events есть такое примечание:
[Note: Access to a field-like event contained within a struct type is not thread-safe. end note]
А так да — для field-like events сгенерированные компилятором аксессоры add_XXX и remove_XXX
помечаются атрибутом [MethodImpl(MethodImplOptions.Synchronized)]
Specifies that the method can be executed by only one thread at a time. Static methods lock on the type, while instance methods lock on the instance. Only one thread can execute in any of the instance functions and only one thread can execute in any of a class's static functions.
Здравствуйте, nikov, Вы писали:
_FR>>cо структурой таких проблем нет — невозможно получить две ссылки на одну и ту же структуру :о))
N>А представь, что структура является полем объекта или элементом массива, к которому идет доступ из разных потоков.
namespace ConsoleApplication1
{
using System;
using System.Diagnostics;
internal class Program
{
private struct Test
{
public event EventHandler Event;
public void Raise()
{
if(Event != null)
{
Event(this, EventArgs.Empty);
}//if
}
}
private readonly Test test;
static void Main(string[] args)
{
Program p = new Program();
p.test.Event += delegate { Debug.Print("Test 1"); };
p.test.Event += delegate { Debug.Print("Test 2"); };
p.test.Raise();
Test[] tests = new Test[1];
tests[0].Event += delegate { Debug.Print("Test 3"); };
tests[0].Event += delegate { Debug.Print("Test 4"); };
tests[0].Raise();
}
}
}
Сработает только вариант с массивом (третий и четвёртый тесты). Спасибо, не подумал.
В первом же случае (поле объекта) ни один Print не вызовется.
Help will always be given at Hogwarts to those who ask for it.
N>Поле-то ссылочного типа, а значит, операции чтения-записи являются атомарными.
N>Ecma-334, 12.5 Atomicity of variable references N>
N>Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort,
N>uint, int, float, and reference types.
N>Другое дело, что при некоторых моделях памяти, изменение, сделанное в одном потоке, может быть не сразу видно в другом. Но речь может идти только о запаздывании, а не о нарушении целостности. В этом случае барьер, создаваемый явной блокировкой, может сыграть роль.
да, спасибо. запаздывание не критично, главное чтобы прочитанное значение ссылки было валидным.
_FR>Разве что тем, что lock в приведённом примере не нужен
lock нужен затем, что пока он готовит мне список invokations кто-то другой подпишется и он вылетит с ошибкой итерации как минимум а скорее всего изза ошибки синхронизации
Здравствуйте, _uncle, Вы писали:
_FR>>Разве что тем, что lock в приведённом примере не нужен
_>lock нужен затем, что пока он готовит мне список invokations кто-то другой подпишется и он вылетит с ошибкой итерации как минимум а скорее всего изза ошибки синхронизации
Делегат — объект немодифицируемый. Где может вылететь-то?
Здравствуйте, _FRED_, Вы писали:
_FR>В первом же случае (поле объекта) ни один Print не вызовется.
Кажется, ты нашел ошибку в компиляторе.
Обычно при попытке менять свойство свойства (если первое свойство имеет значимый тип), выдается ошибка на этапе компиляции. Потому что иначе изменения все равно не отобразятся в исходном объекте, а программист может быть введен в заблуждение. А с событиями, оказывается, такое прокатывает. Надо будет внимательнее посмотреть, является ли это пробелом в спецификации, или просто ошибка в реализации компилятора.
R>А так да — для field-like events сгенерированные компилятором аксессоры add_XXX и remove_XXX R>помечаются атрибутом [MethodImpl(MethodImplOptions.Synchronized)]
R>
R>Specifies that the method can be executed by only one thread at a time. Static methods lock on the type, while instance methods lock on the instance. Only one thread can execute in any of the instance functions and only one thread can execute in any of a class's static functions.
это всё касается только C# 3.0? т.е. в 2.0 надо ручками блокировки таки делать?
_FR>>Разве что тем, что lock в приведённом примере не нужен
_>lock нужен затем, что пока он готовит мне список invokations кто-то другой подпишется и он вылетит с ошибкой итерации как минимум а скорее всего изза ошибки синхронизации
не, так не будет. Когда кто-то захочет подписаться, будет создан новый делегат, а старый останется нетронутым.
X>>это всё касается только C# 3.0? т.е. в 2.0 надо ручками блокировки таки делать?
N>Нет-нет, это все было так еще с версии 1.0. Просто я заглянул в тот документ, который у меня под рукой.
хм, это многое упрощает. Вопрос не по теме: как посмотреть, помечены ни методы правильным атрибутом? ildasm что-то не показывает...
.event WLPTPCommunications.WLPTPServerTransportReceivedRequestDelegate ReceivedRequest
{
.addon instance void WLPTPCommunications.WLPTPServerTransport::add_ReceivedRequest(class WLPTPCommunications.WLPTPServerTransportReceivedRequestDelegate)
.removeon instance void WLPTPCommunications.WLPTPServerTransport::remove_ReceivedRequest(class WLPTPCommunications.WLPTPServerTransportReceivedRequestDelegate)
} // end of event WLPTPServerTransport::ReceivedRequest
Здравствуйте, xpg934, Вы писали:
X>не, так не будет. Когда кто-то захочет подписаться, будет создан новый делегат, а старый останется нетронутым.
То есть каждый раз при m_ReceivedRequestHandler += value меняется instance m_ReceivedRequestHandler ? надо проверить... не знаю, может она еще и атомарная ?
Здравствуйте, _uncle, Вы писали:
X>>не, так не будет. Когда кто-то захочет подписаться, будет создан новый делегат, а старый останется нетронутым.
_>То есть каждый раз при m_ReceivedRequestHandler += value меняется instance m_ReceivedRequestHandler ? надо проверить... не знаю, может она еще и атомарная ?
Нет, instance, на который указывает m_ReceivedRequestHandler, не меняется. Меняется значение поля m_ReceivedRequestHandler (ему присваивается созданный делегат).
X>>не, так не будет. Когда кто-то захочет подписаться, будет создан новый делегат, а старый останется нетронутым.
_>То есть каждый раз при m_ReceivedRequestHandler += value меняется instance m_ReceivedRequestHandler ?
точно так. Т.к. однажды созданный делегат никогда больше не меняется.
_>надо проверить... не знаю, может она еще и атомарная ?
не, но вот как раз копаю чтобы убедиться, что += и -= по умолчанию thread-safe
Здравствуйте, xpg934, Вы писали:
X>хм, это многое упрощает. Вопрос не по теме: как посмотреть, помечены ни методы правильным атрибутом? ildasm что-то не показывает...
X>
Здравствуйте, xpg934, Вы писали:
X>смотрю... и ничего похожего на synchronized:
synchronized добавляется только для автоматически созданных аксессоров у field-like event. Если add/remove пишется вручную, то о синхронизации надо заботиться самому.