Как стать автором
Обновить

Комментарии 30

>> Отличный бесплатный видео урок на эту тему от Александра.
«Вы, как начинающие программисты...»
«Почему они назвали эту машину „Машиной состояний“ вам сейчас будет пока непонятно, но подождите, я сейчас всё объясню»
и т.д.
Как же мне не нравятся презентаторы, считающие себя умнее других… Если честно, он напоминает того самого брата из фильма Брат 2, не в обиду будет сказано :)
Через Task TResult.ConfigureAwait можно управлять в каком именно потоке должен вернуться результат работы.

Если вкратце, то нет, нельзя.

Возьметесь доказать свое утверждение?
Отчасти так, детали не документированы явно. Передавая FALSE мы лишь сообщим что на с не заботит где выполниться продолжение, а не использовать специфический контекст.
Вообще не так.

Когда вы передаете false вы говорите, что система не должна пытаться вернуть выполнение в контекст. Когда вы передаете true (или, что эквивалентно, не вызываете метод), система будет пытаться вернуть вас в контекст, который был на момент запуска метода (если какой-то был), что в определенных случаях приводит к дедлокам.

Но, что важно: это все не имеет никакого отношения к потокам, только к контексту. Яркий пример — asp.net, где выполнение скачет между потоками как угодно, но движок следит за тем, чтобы весь конекст сохранялся. Только на этой неделе отлавливали связанные с этим баги.
Согласен что сложно, именно поэтому «По разному ведет себя в консолях, WinForms, WPF…».
Не имеет отношения к потокам? Тогда зачем Task и пул.
Не мешайте все в одну кучу. Если вы очень хорошо знаете работу этого типа в контексте aps.net, то это не распространяется в равной степени на другие его формы.
Согласен что сложно, именно поэтому «По разному ведет себя в консолях, WinForms, WPF…».

На самом деле, формально — нет. Ведет себя строго одинаково — возвращает (или нет) на контекст. Это контексты отличаются.

Не имеет отношения к потокам? Тогда зачем Task и пул.

Пула там (явно) нет, есть только таск. А таск там именно затем, чтобы абстрагироваться от потоков. Task, async/await, вообще TPL — это абстракции более высокого порядка, нежели потоки.

Если вы очень хорошо знаете работу этого типа в контексте aps.net, то это не распространяется в равной степени на другие его формы.

Это не работа контекста, это работа рантайма. Он и без контекста себя так ведет.
«Люди видят то, что хотят видеть; слышат то, что хотят слышать; верят в то, во что хотят верить и отказываются верить в то, что им не нравится.» Скилеф

Игра слов, вы безусловно правы, согласен с вами.
Вы считаете разницу между потоком и контекстом — игрой слов?

Казалось бы, простой пример:

Thread.CurrentPrincipal = GePrincipal();
if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
    await DoSomeWork(); //continuation will run on captured context
    Debug.Assert(Thread.CurrentPrincipal.Identity.IsAutenticated); //WTF?!
}
Вы серьезно?
В контексте asp.net имеется ввиду сам asp.net.
Я совершенно серьезно, потому что везде в этой дискуссии я имел в виду другой контекст.
Согласен с вами, в asp.net поведение отличается от консольного или того же WinForms.

Именно для этого в топике предложение:
«По разному ведет себя в консолях, WinForms, WPF…», можно дописать asp.net, wcf и т.д.

Но изучить и описать в одном пункте сразу все возможные повороты я не могу, тем более что по словам Джона Скита «информация не документирована явно». И когда это средство впервые было представлено публике Хейлсбергом произошло много изменений, и не факт что их не будет в дальнейшем (без нарушения работы существующих решений).

Ваше замечание уместно и справедливо, спасибо что подсказали как оно в asp.net.

P.S. Два года назад я проходил курс внутреннего устройства Windows NT, и думаю что догадываюсь что такое контекст потока.
Согласен с вами, в asp.net поведение отличается от консольного или того же WinForms.

Да нет же. Поведение await везде одинаково — захват контекста, и если тот существует, то continuation будет отправлен на него, а если нет — выполнен в произвольном контексте. Именно поэтому я сразу сказал, что управлять потоком вы не можете никак (разве что вы решите написать свой собственный шедулер/контекст).

Отличается только поведение контекстов синхронизации (и оно в среднем — за исключением консольных приложений — реализовано весьма логично).
Извиняюсь, что влезаю не в тему, но судя по всему, тут собрались спецы в async/await-ах :) Мы в своём проекте используем Reactive Extensions, и в этом фреймворке довольно гибко реализована возможность написания юнит-тестов: можно ввести виртуальное время и исполнять задачи на специальном TestScheduler-е. А как с этим обстоит в TPL? То, что я нагуглил, оставляет довольно грустное впечатление.
Я не видел никаких специальных расширений для тестирования времени в TPL. К сожалению.
Слава богу, разобрались.
Вот только в посте у вас все так же написано ложное утверждение.
Исправил, спасибо.
Думал одно, писал другое.
Оказывается не документирована именно работа метода со значением FALSE.
По разному ведет себя в консолях, WinForms, WPF…

это вообще как?

таски очень завязаны на SyncronizationContext (о котором говорит товарищ lair).

Пишите ли Вы простую либу с асинхронным методом, будьте любезны использовать ConfigureAwait(false).
Вне зависимости от предназначения вашего кода.

WPF, WinForms, ASP.NET, XYZ — все работает абсолютно идентично в вопросе async/await.
Размер поколения «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.
У меня есть эта книжечка, спасибо что напомнили про нее.

В разных источниках разные данные:
Поколение 0 — изначально отводится от 256 Кбайт до 4 Мбайт, далее может меняется динамически. Он зависит от разрядности ОС, размера кэш памяти процессора L2 и L3.

Поколение 1 — немногим больше поколения 0.

Поколение 2 — не ограничен искусственно. 10 mb наверное первое пороговое значение, можно проверить в отладчике размер куч если интересно.

* CLR резервирует блоки памяти (запрашивает у Windows), но не выдает их процессу сразу и полностью. Блока два, один SOH другой LOH, и с ними тоже есть свои тонкости и настройки (подробней в 3 источнике).
Что бы убрать сомнения и разобраться самому, приведу результаты тестов консольного приложения в котором создается 10 млн (+1) Point и затем сразу 10 млн (+1) анонимных типов. Сбор данных идет до запуска циклов и после.

До
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 ).
В принципе, цитата и не утверждает, что Gen 2 ограничен размером ~10MB. Это очевидно. Гораздо более интересно, что означает ~10MB при неограниченном размере Gen 2. В книге подробной информации по этому поводу нет.
Я предполагаю, что это не первый порог, а итеративное значение суммы масс всех объектов, перешедших с Gen 1 в Gen 2 с момента последнего запуска Full GC collects (Gen 2, 1 and 0). Если эта сумма равна или больше ~10MB, то запускается полная сборка мусора, а значение суммы масс обнуляется. И так далее. Но это лишь мои догадки.
Согласен с вами. Я цитату прочитал внимательно уже когда тесты произвел, и понял что вы имели ввиду совсем другое.

P.S. Gen 0 = почти равен L2; Gen 1 = L3. Но об этом потом.
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.
Про сборку мусора… эх двумя словами и не скажешь.

Так массив из System.Double при размере 10 600 элементов (85000 / 8 байт) должен попасть в LOH. Однако это происходит уже при размере 1000+.
Вы число точно написали, или это шутки такие :)

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

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.
у Вас похоже пришел апдейт.
Спасибо, не знал.
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

arrays > 1000 doubles

Если быть более точным, то >= 1000
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории