BB>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?
Хороший вопрос, Вы наверное имеете ввиду Access to modified closure.
Проверил, нет, же, видит прекрасно новые Foo.
И Resharper не ругается на Access to modified closure.
Сделал предположение, что Access to modified closure возникает только если переменная foo будет меняться в том же контексте, в котором создавалась лямбда её использующая:
Foo foo = new Foo();
int i = 0;
Thread checkInitializedThread = new Thread(() =>
{
do
{
if (!foo._initialized1 | !foo._initialized2)
{
Console.WriteLine("Bingo!!!");
}
Console.WriteLine(foo._i);
} while (true);
});
checkInitializedThread.Start();
do
{
foo = new Foo();
foo._i = i;
i++;
} while (true);
class Foo
{
public bool _initialized1;
public bool _initialized2;
public int _i;
public Foo()
{
//Thread.Sleep(10);
_initialized1 = true;
Thread.Sleep(10);
_initialized2 = true;
}
}
Resharper заругался на Access to modified closure. Но checkInitializedThread все-равно видит новые Foo
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
BB>>Вы уверены, что checkInitializedThread увидит значение foo, отличное от первого new Foo()?
IB>Хороший вопрос, Вы наверное имеете ввиду Access to modified closure. IB>Проверил, нет, же, видит прекрасно новые Foo. IB>И Resharper не ругается на Access to modified closure.
Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, bl-blx, Вы писали:
BB>>Нет, я просто не соображу, где там барьер, который гарантирует видимость другим потоком.
S>Под x86/x64 по-прежнему работает правило "all writes are volatile". Под арм/итаниум — с оговорками (см C# Memory Model Implementation on ARM).
Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?
Здравствуйте, bl-blx, Вы писали:
BB>Да, спасибо — забыл об этом, но, тем не менее, второй поток делает non-volatile read — с чего бы ему в main memory лезть?
Это нужно спрашивать у специалистов, если верить stackoverflow — под x86/64 обновление кэша берёт на себя сам процессор.
Re[7]: Стоит ли переносить инициализацию в отдельный метод?
Здравствуйте, bl-blx, Вы писали:
BB>второй поток делает non-volatile read — с чего бы ему в main memory лезть?
Совершенно верно. В потоке checkInitializedThread и foo, и foo._initialized1, и foo._initialized2, и foo._i могут быть закэшированы. Сделать это способна любая фаза кодогенерации: IL, JIT и т.д.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
Здравствуйте, abibok, Вы писали:
A>>>Для исключения в конструкторе это не подойдет. A>>Ну тогда и твой вариант не подойдет
A>А мой как раз подойдет.
В чем будет отличие от моего?
A>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает.
И на каждый вызов делает компиляцию создаваемого Expression-а?
Re[7]: Конструктор не бросает исключений, не связанных с проверкой аргументов
IB>А Initialize(...) тоже не может выкидывать исключения не связанные с проверкой аргументов?
Может, это же обычный метод. И его исключения не завернут в TargetInvocationException.
IB>Проверка аргументов понятие растяжимое. IB>Она может быть любой сложности.
Здесь нет абсолютного решения — только конструктор или только отдельный метод. Нужно смотреть по ситуации. Я просто показал, что есть ситуации, когда наличие метода оправдано.
Re[11]: Стоит ли переносить инициализацию в отдельный метод?
A>>А мой как раз подойдет. A>В чем будет отличие от моего?
В вашем случае (кстати, дайте пример вашего кода) исключение в конструкторе ловится внутри CLR, заворачивается в TargetInvocationException и перебрасывается. Все, куча информации потеряна. Например, значения локальных переменных в момент бросания уже не получить.
A>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает. A>И на каждый вызов делает компиляцию создаваемого Expression-а?
Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
Re[8]: Стоит ли переносить инициализацию в отдельный метод?
Здравствуйте, abibok, Вы писали:
S>>Или просто посмотреть содержимое ex.InnerException
A>Вы не совсем понимаете глубину проблемы. В моей задаче InnerException не поможет.
Задачу вы пока не озвучили
Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.
Re[12]: Стоит ли переносить инициализацию в отдельный метод?
Здравствуйте, abibok, Вы писали: A>В вашем случае (кстати, дайте пример вашего кода) исключение в конструкторе ловится внутри CLR, заворачивается в TargetInvocationException и перебрасывается. Все, куча информации потеряна. Например, значения локальных переменных в момент бросания уже не получить.
О чем это ты, можно поподробнее?
Вот код:
Скрытый текст
public static class ExceptionHelper
{
private static readonly Action<Exception> _preserveInternalException;
static ExceptionHelper()
{
var preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
_preserveInternalException = (Action<Exception>)Delegate.CreateDelegate(typeof(Action<Exception>), preserveStackTrace);
}
public static Exception PreserveStackTrace(this Exception ex)
{
_preserveInternalException(ex);
return ex;
}
}
public class A
{
public A(int x, string s)
{
throw new ArgumentException("s");
}
}
static class Program
{
public static object CreateInstance(Type type, object[] arguments)
{
try
{
return Activator.CreateInstance(type, arguments);
}
catch (TargetInvocationException ex)
{
throw ex.InnerException.PreserveStackTrace();
}
}
private static void Main()
{
try
{
//var a = new A(42, "Hi");var a = CreateInstance(typeof(A), new object[] { 42, "Hi" });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
В консоль выводится:
System.ArgumentException: s
at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80
A>>>Я плотно занимался этой темой, знаю кучу нюансов по поводу бросания и перехвата исключений, и этот код сейчас работает. A>>И на каждый вызов делает компиляцию создаваемого Expression-а? A>Да. Это большая проблема? Мы не обязаны пользоваться этой функцией для создания абсолютно всех объектов. А там где это необходимо, потеря производительности (не такая уж и большая) оправдана.
А че б не закэшировать активаторы? Ну да фиг с ним...
Re[9]: Стоит ли переносить инициализацию в отдельный метод?
Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.
S>Если как написано в другом посте, проблема в отладке — в настройках (debug-exceptions) можно включить прерывание отладки в момент броска исключения.
Речь идет не только об отладке, но и о штатной работе программы. Плюс такая настройка будет прерывать отладку для любых исключений, а нас интересуют только те, которые unhandled.
Re[13]: Стоит ли переносить инициализацию в отдельный метод?
Представьте себе, что конструктор не такой простой, а имеет сложную внутреннюю логику, которая зависит от состояния членов класса, параметров и значений локальных переменных.
Мы хотим всю эту информацию сохранить в лог, а разработчику предоставить managed dump, который будет указывать прямо на исходное исключение, а не на место, где это исключение поймали и перевыбросили.
Более того, мы хотим иметь не только стек вызовов, который привел к исключению — А(B(C())), но и историю выполнения — X->Y->Z->A(B(C())). И если исключение произошло из-за значения члена класса _myField, то проследить по истории до места, где _myField стал таким, и это место тоже включить в лог. А потом автоматически сделать root cause analysis "из-за проблемы в методе X у нас возникают исключения в C, T и Q".
Это общая задача, которая должна решаться для любого метода. Просто конструкторы немного особенные и создание объектов при таком подходе требуют специального обращения. A>В консоль выводится: A>
A>System.ArgumentException: s
A> at ConsoleApplication46.A..ctor(Int32 x, String s) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 34
A> at ConsoleApplication46.Program.CreateInstance(Type type, Object[] arguments) in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 56
A> at ConsoleApplication46.Program.Main() in d:\tmp\ConsoleApplication46\ConsoleApplication46\Program.cs:line 80
Мне кажется или у вас с номерами строк не все в порядке?
A>А че б не закэшировать активаторы? Ну да фиг с ним...
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, HowardLovekraft, Вы писали:
HL>>Если в какой-то момент времени выполняется конструктор объекта, это значит, что он очень даже достижим.
S>Необязательно. S>http://ericlippert.com/2013/06/10/construction-destruction/
Я правильно понимаю, что в общем виде мы не можем обеспечить гарантию освобождения unmanaged ресурсов?
void F()
{
new SomeClassWithUnmanagedResources();
}
Finalizer может закончить работу до того, как в конструкторе будут выделены и сохранены в полях unmanaged ресурсы, так?
Мне, как разработчику SomeClassWithUnmanagedResources, что делать, чтобы обеспечить гарантию их освобождения?
Re[10]: Стоит ли переносить инициализацию в отдельный метод?
Здравствуйте, abibok, Вы писали:
S>>Задачу вы пока не озвучили
A>Задача — сохранить максимум информации о контексте приложения в момент возникновения исключительной ситуации для логирования и последующей отладки. Это включает в себя правильный callstack со всей информацией, включая значения локальных переменных и параметров функций в каждом фрейме, а не только текстовое представление стека вызовов. Для этого исключение нужно перехватывать сразу после того, как оно было брошено и до того, как оно войдет в блок catch.