Комментарии 30
>> Отличный бесплатный видео урок на эту тему от Александра.
«Вы, как начинающие программисты...»
«Почему они назвали эту машину „Машиной состояний“ вам сейчас будет пока непонятно, но подождите, я сейчас всё объясню»
и т.д.
Как же мне не нравятся презентаторы, считающие себя умнее других… Если честно, он напоминает того самого брата из фильма Брат 2, не в обиду будет сказано :)
«Вы, как начинающие программисты...»
«Почему они назвали эту машину „Машиной состояний“ вам сейчас будет пока непонятно, но подождите, я сейчас всё объясню»
и т.д.
Как же мне не нравятся презентаторы, считающие себя умнее других… Если честно, он напоминает того самого брата из фильма Брат 2, не в обиду будет сказано :)
+4
Через Task TResult.ConfigureAwait можно управлять в каком именно потоке должен вернуться результат работы.
Если вкратце, то нет, нельзя.
Возьметесь доказать свое утверждение?
+2
Отчасти так, детали не документированы явно. Передавая FALSE мы лишь сообщим что на с не заботит где выполниться продолжение, а не использовать специфический контекст.
-1
Вообще не так.
Когда вы передаете
Но, что важно: это все не имеет никакого отношения к потокам, только к контексту. Яркий пример — asp.net, где выполнение скачет между потоками как угодно, но движок следит за тем, чтобы весь конекст сохранялся. Только на этой неделе отлавливали связанные с этим баги.
Когда вы передаете
false
вы говорите, что система не должна пытаться вернуть выполнение в контекст. Когда вы передаете true
(или, что эквивалентно, не вызываете метод), система будет пытаться вернуть вас в контекст, который был на момент запуска метода (если какой-то был), что в определенных случаях приводит к дедлокам.Но, что важно: это все не имеет никакого отношения к потокам, только к контексту. Яркий пример — asp.net, где выполнение скачет между потоками как угодно, но движок следит за тем, чтобы весь конекст сохранялся. Только на этой неделе отлавливали связанные с этим баги.
+1
Согласен что сложно, именно поэтому «По разному ведет себя в консолях, WinForms, WPF…».
Не имеет отношения к потокам? Тогда зачем Task и пул.
Не мешайте все в одну кучу. Если вы очень хорошо знаете работу этого типа в контексте aps.net, то это не распространяется в равной степени на другие его формы.
Не имеет отношения к потокам? Тогда зачем Task и пул.
Не мешайте все в одну кучу. Если вы очень хорошо знаете работу этого типа в контексте aps.net, то это не распространяется в равной степени на другие его формы.
0
Согласен что сложно, именно поэтому «По разному ведет себя в консолях, WinForms, WPF…».
На самом деле, формально — нет. Ведет себя строго одинаково — возвращает (или нет) на контекст. Это контексты отличаются.
Не имеет отношения к потокам? Тогда зачем Task и пул.
Пула там (явно) нет, есть только таск. А таск там именно затем, чтобы абстрагироваться от потоков. Task, async/await, вообще TPL — это абстракции более высокого порядка, нежели потоки.
Если вы очень хорошо знаете работу этого типа в контексте aps.net, то это не распространяется в равной степени на другие его формы.
Это не работа контекста, это работа рантайма. Он и без контекста себя так ведет.
+2
«Люди видят то, что хотят видеть; слышат то, что хотят слышать; верят в то, во что хотят верить и отказываются верить в то, что им не нравится.» Скилеф
Игра слов, вы безусловно правы, согласен с вами.
Игра слов, вы безусловно правы, согласен с вами.
-3
Вы считаете разницу между потоком и контекстом — игрой слов?
Казалось бы, простой пример:
Казалось бы, простой пример:
Thread.CurrentPrincipal = GePrincipal();
if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
await DoSomeWork(); //continuation will run on captured context
Debug.Assert(Thread.CurrentPrincipal.Identity.IsAutenticated); //WTF?!
}
+3
Согласен с вами, в asp.net поведение отличается от консольного или того же WinForms.
Именно для этого в топике предложение:
«По разному ведет себя в консолях, WinForms, WPF…», можно дописать asp.net, wcf и т.д.
Но изучить и описать в одном пункте сразу все возможные повороты я не могу, тем более что по словам Джона Скита «информация не документирована явно». И когда это средство впервые было представлено публике Хейлсбергом произошло много изменений, и не факт что их не будет в дальнейшем (без нарушения работы существующих решений).
Ваше замечание уместно и справедливо, спасибо что подсказали как оно в asp.net.
P.S. Два года назад я проходил курс внутреннего устройства Windows NT, и думаю что догадываюсь что такое контекст потока.
Именно для этого в топике предложение:
«По разному ведет себя в консолях, WinForms, WPF…», можно дописать asp.net, wcf и т.д.
Но изучить и описать в одном пункте сразу все возможные повороты я не могу, тем более что по словам Джона Скита «информация не документирована явно». И когда это средство впервые было представлено публике Хейлсбергом произошло много изменений, и не факт что их не будет в дальнейшем (без нарушения работы существующих решений).
Ваше замечание уместно и справедливо, спасибо что подсказали как оно в asp.net.
P.S. Два года назад я проходил курс внутреннего устройства Windows NT, и думаю что догадываюсь что такое контекст потока.
-2
Согласен с вами, в asp.net поведение отличается от консольного или того же WinForms.
Да нет же. Поведение
await
везде одинаково — захват контекста, и если тот существует, то continuation будет отправлен на него, а если нет — выполнен в произвольном контексте. Именно поэтому я сразу сказал, что управлять потоком вы не можете никак (разве что вы решите написать свой собственный шедулер/контекст).Отличается только поведение контекстов синхронизации (и оно в среднем — за исключением консольных приложений — реализовано весьма логично).
+2
Извиняюсь, что влезаю не в тему, но судя по всему, тут собрались спецы в async/await-ах :) Мы в своём проекте используем Reactive Extensions, и в этом фреймворке довольно гибко реализована возможность написания юнит-тестов: можно ввести виртуальное время и исполнять задачи на специальном TestScheduler-е. А как с этим обстоит в TPL? То, что я нагуглил, оставляет довольно грустное впечатление.
0
Слава богу, разобрались.
0
По разному ведет себя в консолях, WinForms, WPF…
это вообще как?
таски очень завязаны на SyncronizationContext (о котором говорит товарищ lair).
Пишите ли Вы простую либу с асинхронным методом, будьте любезны использовать ConfigureAwait(false).
Вне зависимости от предназначения вашего кода.
WPF, WinForms, ASP.NET, XYZ — все работает абсолютно идентично в вопросе async/await.
0
Размер поколения «2» не ограничивается искусственно, используется вся доступная память (которой Windows может поделиться с CLR), но GC не ждет ее полного заполнения а использует пороговые значения (какие не знаю).
Цитата из «Under the Hood of .NET Management» от Red-Gate:
The GC runs automatically on a separate thread under one of the conditions below.
• When the size of objects in any generation reaches a generation-specific threshold. To
be precise, when:
• Gen 0 hits ~256 K
• Gen 1 hits ~ 2 MB (at which point the GC collects Gen 1 and 0)
• Gen 2 hits ~10 MB (at which point the GC collects Gen 2, 1 and 0)
• GC.Collect() is called in code
• the OS sends a low memory notification.
+2
У меня есть эта книжечка, спасибо что напомнили про нее.
В разных источниках разные данные:
Поколение 0 — изначально отводится от 256 Кбайт до 4 Мбайт, далее может меняется динамически. Он зависит от разрядности ОС, размера кэш памяти процессора L2 и L3.
Поколение 1 — немногим больше поколения 0.
Поколение 2 — не ограничен искусственно. 10 mb наверное первое пороговое значение, можно проверить в отладчике размер куч если интересно.
* CLR резервирует блоки памяти (запрашивает у Windows), но не выдает их процессу сразу и полностью. Блока два, один SOH другой LOH, и с ними тоже есть свои тонкости и настройки (подробней в 3 источнике).
В разных источниках разные данные:
Поколение 0 — изначально отводится от 256 Кбайт до 4 Мбайт, далее может меняется динамически. Он зависит от разрядности ОС, размера кэш памяти процессора L2 и L3.
Поколение 1 — немногим больше поколения 0.
Поколение 2 — не ограничен искусственно. 10 mb наверное первое пороговое значение, можно проверить в отладчике размер куч если интересно.
* CLR резервирует блоки памяти (запрашивает у Windows), но не выдает их процессу сразу и полностью. Блока два, один SOH другой LOH, и с ними тоже есть свои тонкости и настройки (подробней в 3 источнике).
0
Что бы убрать сомнения и разобраться самому, приведу результаты тестов консольного приложения в котором создается 10 млн (+1) Point и затем сразу 10 млн (+1) анонимных типов. Сбор данных идет до запуска циклов и после.
Gen2 232872512 — 222 Mb.
Данные проверял в Windbg и Process Explorer, консольное приложение собрано в режиме x64.
Оборудование:
Windows 8.1 64, i7-4702MQ ( L2 — 4x 256 Kb, L3 — 6 Mb ).
До
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000000c200001030
generation 1 starts at 0x000000c200001018
generation 2 starts at 0x000000c200001000
ephemeral segment allocation context: none
segment begin allocated size
000000c200000000 000000c200001000 000000c200005fe8 0x4fe8(20456)
Large object heap starts at 0x000000c210001000
segment begin allocated size
000000c210000000 000000c210001000 000000c210009728 0x8728(34600)
Total Size: Size: 0xd710 (55056) bytes.
------------------------------
GC Heap Size: Size: 0xd710 (55056) bytes.
0:003> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 20408 24 24 34600
Free space: Percentage
Heap0 24 0 0 152SOH: 0% LOH: 0%
После
0:006> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000000c20e416a70
generation 1 starts at 0x000000c20de16a40
generation 2 starts at 0x000000c200001000
ephemeral segment allocation context: none
segment begin allocated size
000000c200000000 000000c200001000 000000c20e4f9fe8 0xe4f8fe8(240095208)
Large object heap starts at 0x000000c210001000
segment begin allocated size
000000c210000000 000000c210001000 000000c210009728 0x8728(34600)
000000c218000000 000000c218001000 000000c220001040 0x8000040(134217792)
Total Size: Size: 0x16501750 (374347600) bytes.
------------------------------
GC Heap Size: Size: 0x16501750 (374347600) bytes.
0:006> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 931192 6291504 232872512 134252392
Free space: Percentage
Heap0 24 24 69984 184SOH: 0% LOH: 0%
Gen2 232872512 — 222 Mb.
Данные проверял в Windbg и Process Explorer, консольное приложение собрано в режиме x64.
Оборудование:
Windows 8.1 64, i7-4702MQ ( L2 — 4x 256 Kb, L3 — 6 Mb ).
0
В принципе, цитата и не утверждает, что Gen 2 ограничен размером ~10MB. Это очевидно. Гораздо более интересно, что означает ~10MB при неограниченном размере Gen 2. В книге подробной информации по этому поводу нет.
Я предполагаю, что это не первый порог, а итеративное значение суммы масс всех объектов, перешедших с Gen 1 в Gen 2 с момента последнего запуска Full GC collects (Gen 2, 1 and 0). Если эта сумма равна или больше ~10MB, то запускается полная сборка мусора, а значение суммы масс обнуляется. И так далее. Но это лишь мои догадки.
Я предполагаю, что это не первый порог, а итеративное значение суммы масс всех объектов, перешедших с Gen 1 в Gen 2 с момента последнего запуска Full GC collects (Gen 2, 1 and 0). Если эта сумма равна или больше ~10MB, то запускается полная сборка мусора, а значение суммы масс обнуляется. И так далее. Но это лишь мои догадки.
0
1. LINQ to Object исполняется в JIT как обычные делегаты (внутрипроцессный запрос), LINQ to SQL строит дерево выражений и выполняет его уже SQL, или любая другая среда. Дерево может быть переведено в делегаты.
Не все так просто :)
LINQ to SQL — есть тот же самый LINQ to Everything, правда подставляется свой провайдер IQueryable. исполняет его CLR (ваш K.O).
Дерево переводится в таких случаях для делегаты с переписыванием самого дерева (привет CompiledQuery).
SQL генерируется и передается в ADO.NET или xyz.
Поэтому всегда можно создать LINQ to anything.
А так, да, дерево выражений всегда можно перевести в делегат через LambdaExpression.
0
Про сборку мусора… эх двумя словами и не скажешь.
Так массив из System.Double при размере 10 600 элементов (85000 / 8 байт) должен попасть в LOH. Однако это происходит уже при размере 1000+.
Так массив из System.Double при размере 10 600 элементов (85000 / 8 байт) должен попасть в LOH. Однако это происходит уже при размере 1000+.
0
Вы число точно написали, или это шутки такие :)
10 000
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000002a13ec1030
generation 1 starts at 0x0000002a13ec1018
generation 2 starts at 0x0000002a13ec1000
ephemeral segment allocation context: none
segment begin allocated size
0000002a13ec0000 0000002a13ec1000 0000002a13edb898 0x1a898(108696)
Large object heap starts at 0x0000002a23ec1000
segment begin allocated size
0000002a23ec0000 0000002a23ec1000 0000002a23ec9728 0x8728(34600)
Total Size: Size: 0x22fc0 (143296) bytes.
------------------------------
GC Heap Size: Size: 0x22fc0 (143296) bytes.
0:003> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 108648 24 24 34600
Free space: Percentage
Heap0 24 0 0 152SOH: 0% LOH: 0%
0:003> !dumpheap 0x0000002a23ec1000
Address MT Size
0000002a23ec1000 0000002a11e9b380 24 Free
0000002a23ec1018 0000002a11e9b380 30 Free
0000002a23ec1038 00007ffefe1b4918 8848
0000002a23ec32c8 0000002a11e9b380 30 Free
0000002a23ec32e8 00007ffefe1b4918 1056
0000002a23ec3708 0000002a11e9b380 30 Free
0000002a23ec3728 00007ffefe1b4918 8192
0000002a23ec5728 0000002a11e9b380 30 Free
0000002a23ec5748 00007ffefe1b4918 16352
Statistics:
MT Count TotalSize Class Name
0000002a11e9b380 5 144 Free
00007ffefe1b4918 4 34448 System.Object[]
Total 9 objects
0:003> !dumpheap -type System.Double[]
Address MT Size
0000002a13ec6a08 00007ffefe218450 80024
Statistics:
MT Count TotalSize Class Name
00007ffefe218450 1 80024 System.Double[]
Total 1 objects
0:003> !do 0000002a13ec6a08
Name: System.Double[]
MethodTable: 00007ffefe218450
EEClass: 00007ffefdc05138
Size: 80024(0x13898) bytes
Array: Rank 1, Number of elements 10000, Type Double
Fields:
None
11 000
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000000ef228f1030
generation 1 starts at 0x000000ef228f1018
generation 2 starts at 0x000000ef228f1000
ephemeral segment allocation context: none
segment begin allocated size
000000ef228f0000 000000ef228f1000 000000ef228f7fe8 0x6fe8(28648)
Large object heap starts at 0x000000ef328f1000
segment begin allocated size
000000ef328f0000 000000ef328f1000 000000ef3290ef20 0x1df20(122656)
Total Size: Size: 0x24f08 (151304) bytes.
------------------------------
GC Heap Size: Size: 0x24f08 (151304) bytes.
0:003> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 28600 24 24 122656
Free space: Percentage
Heap0 24 0 0 184SOH: 0% LOH: 0%
0:003> !dumpheap 0x000000ef328f1000
Address MT Size
000000ef328f1000 000000ef2090b830 24 Free
000000ef328f1018 000000ef2090b830 30 Free
000000ef328f1038 00007ffefe1b4918 8848
000000ef328f32c8 000000ef2090b830 30 Free
000000ef328f32e8 00007ffefe1b4918 1056
000000ef328f3708 000000ef2090b830 30 Free
000000ef328f3728 00007ffefe1b4918 8192
000000ef328f5728 000000ef2090b830 30 Free
000000ef328f5748 00007ffefe1b4918 16352
000000ef328f9728 000000ef2090b830 30 Free
000000ef328f9748 00007ffefe218450 88024
Statistics:
MT Count TotalSize Class Name
000000ef2090b830 6 174 Free
00007ffefe1b4918 4 34448 System.Object[]
00007ffefe218450 1 88024 System.Double[]
Total 11 objects
0:003> !dumpheap -type System.Double[]
Address MT Size
000000ef328f9748 00007ffefe218450 88024
Statistics:
MT Count TotalSize Class Name
00007ffefe218450 1 88024 System.Double[]
Total 1 objects
0:003> !do 000000ef328f9748
Name: System.Double[]
MethodTable: 00007ffefe218450
EEClass: 00007ffefdc05138
Size: 88024(0x157d8) bytes
Array: Rank 1, Number of elements 11000, Type Double
Fields:
None
10 050
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000009146561030
generation 1 starts at 0x0000009146561018
generation 2 starts at 0x0000009146561000
ephemeral segment allocation context: none
segment begin allocated size
0000009146560000 0000009146561000 000000914657ba28 0x1aa28(109096)
Large object heap starts at 0x0000009156561000
segment begin allocated size
0000009156560000 0000009156561000 0000009156569728 0x8728(34600)
Total Size: Size: 0x23150 (143696) bytes.
------------------------------
GC Heap Size: Size: 0x23150 (143696) bytes.
0:003> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 109048 24 24 34600
Free space: Percentage
Heap0 24 0 0 152SOH: 0% LOH: 0%
0:003> !dumpheap 0x0000009156561000
Address MT Size
0000009156561000 000000914483b920 24 Free
0000009156561018 000000914483b920 30 Free
0000009156561038 00007ffefe1b4918 8848
00000091565632c8 000000914483b920 30 Free
00000091565632e8 00007ffefe1b4918 1056
0000009156563708 000000914483b920 30 Free
0000009156563728 00007ffefe1b4918 8192
0000009156565728 000000914483b920 30 Free
0000009156565748 00007ffefe1b4918 16352
Statistics:
MT Count TotalSize Class Name
000000914483b920 5 144 Free
00007ffefe1b4918 4 34448 System.Object[]
Total 9 objects
0:003> !dumpheap -type System.Double[]
Address MT Size
0000009146566a08 00007ffefe218450 80424
Statistics:
MT Count TotalSize Class Name
00007ffefe218450 1 80424 System.Double[]
Total 1 objects
0:003> !do 0000009146566a08
Name: System.Double[]
MethodTable: 00007ffefe218450
EEClass: 00007ffefdc05138
Size: 80424(0x13a28) bytes
Array: Rank 1, Number of elements 10050, Type Double
Fields:
None
0
In 32-bit architrctures CLR’s execution engine attempts to place these arrays > 1000 doubles on the LOH because of the performance benefit of accessing aligned doubles. However there are no benefits of applying the same heuristics on 64-bit architectures because doubles are already aligned on an 8-byte boundary. As such we have disabled this heuristics for 64-bit architectures in .,NET 4.5
Thanks,
Abhishek Mondal
Program Manager
Common Language Runtime
http://blogs.msdn.com/b/dotnet/archive/2011/10/04/large-object-heap-improvements-in-net-4-5.aspx
и еще
http://stackoverflow.com/questions/11791038/large-object-heap-fragmentation-issues-with-arrays
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266330&wa=wsignin1.0
p.s.
у Вас похоже пришел апдейт.
0
Спасибо, не знал.
x32 .NET 4.5
x32 .NET 4.5
1001
0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x02c01018
generation 1 starts at 0x02c0100c
generation 2 starts at 0x02c01000
ephemeral segment allocation context: none
segment begin allocated size
02c00000 02c01000 02c05ff4 0x4ff4(20468)
Large object heap starts at 0x03c01000
segment begin allocated size
03c00000 03c01000 03c07300 0x6300(25344)
Total Size: Size: 0xb2f4 (45812) bytes.
------------------------------
GC Heap Size: Size: 0xb2f4 (45812) bytes.
0:003> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 20444 12 12 25344
Free space: Percentage
Heap0 12 0 0 96SOH: 0% LOH: 0%
0:003> !dumpheap -type System.Double[]
Address MT Size
03c053a8 70bafe70 8020
Statistics:
MT Count TotalSize Class Name
70bafe70 1 8020 System.Double[]
Total 1 objects
0:003> !do 03c053a8
Name: System.Double[]
MethodTable: 70bafe70
EEClass: 70811530
Size: 8020(0x1f54) bytes
Array: Rank 1, Number of elements 1001, Type Double
Fields:
None
0:003> !dumpheap 0x03c01000
Address MT Size
03c01000 01230af8 10 Free
03c01010 01230af8 14 Free
03c01020 70b60cbc 4424
03c02168 01230af8 14 Free
03c02178 70b60cbc 528
03c02388 01230af8 14 Free
03c02398 70b60cbc 4096
03c03398 01230af8 14 Free
03c033a8 70b60cbc 8176
03c05398 01230af8 14 Free
03c053a8 70bafe70 8020
Statistics:
MT Count TotalSize Class Name
01230af8 6 80 Free
70bafe70 1 8020 System.Double[]
70b60cbc 4 17224 System.Object[]
Total 11 objects
0
arrays > 1000 doubles
Если быть более точным, то >= 1000
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Интересные заметки по C# и CLR (v2.0)