Время потока, затраченное на синхронизацию, слишком велико?

сегодня я профилировал одно из моих приложений на C# с помощью анализатора производительности Visual Studio 2010. В частности, я профилировал для "параллелизм" потому что казалось, что мое приложение должно иметь больше возможностей, то это демонстрировала. Отчет об анализе показал, что потоки тратят ~70-80% своего времени в состоянии синхронизации.

честно говоря, я не уверен, что это означает. Означает ли это, что приложение страдает от live-lock состояние?

для контекста... есть ~30 + длинных потоков, связанных с одним AppDomain (если это имеет значение) и некоторые из потоков очень заняты (например. while(true) { _waitEvent.WaitOne(0); //do stuff }).

Я понимаю, что это довольно расплывчатый вопрос... Думаю,я ищу некоторое разъяснение о значении состояния синхронизации потоков. Сколько это слишком много, а почему? ~75% действительно плохо? У меня слишком много нитей? или мне просто начать искать в других областях?

3 ответов


Я не уверен, что это означает.

это означает, что потоки в среднем тратили 75% своего времени, ожидая, пока другой поток закончит некоторую работу.

означает ли это, что приложение страдает от условия live-lock?

может быть!

чтобы уточнить для читателей, незнакомых с термином: "тупик" - это когда два потока ждут друг друга, чтобы закончить, и поэтому они ждать вечно. "Живая блокировка" - это ситуация, когда два потока пытаются избежать тупика, но из-за их плохого выбора проводят большую часть своего времени в ожидании. Представьте себе, например, стол с двумя людьми, вилкой и ножом. Оба человека хотят взять обе посуды, использовать их, а затем положить их вниз. Предположим, я возьму нож, а ты вилку. Если мы оба решим подождать, пока другой положит посуду, мы окажемся в тупике. Если мы оба поймем, что зашли в тупик, и Я положил нож, ты положил вилку и ... -- Тогда я возьму вилку, а ты возьмешь нож!--14-->, мы заблокированы. Мы можем бесконечно повторять этот процесс; мы оба работаем над разрешением ситуации, но мы недостаточно эффективно общаемся, чтобы действительно решить ее быстро.

однако, мой Угадай это то, что вы не в ситуации живого замка. Я предполагаю, что у вас просто есть огромное мнение о небольшом количестве критических ресурсы, к которым можно получить доступ только по одному потоку за раз. Бритва Оккама указала бы, что вы должны принять простую гипотезу-множество нитей по очереди, используя скудный ресурс-вместо сложной гипотезы-целая куча нитей, пытающихся сказать друг другу: "нет, ты идешь первым".

есть ~30 + длинных потоков, привязанных к одному AppDomain (если это имеет значение), и некоторые из потоков очень заняты (например. в то время как(правда) { _waitEvent.WaitOne (0); //do stuff }).

звучит ужасно.

Я понимаю, что это довольно расплывчатый вопрос.

Да, это так.

сколько это слишком много, а почему?

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

это ~75% действительно плохо?

ваши потоки занимают в четыре раза больше времени, чем им нужно. Мне это не кажется слишком хорошим.

У меня слишком много потоков?

почти наверняка, да. 30-это много.

но это совершенно не технический вопрос задать в вашей ситуации. Спрашивать " do У меня слишком много нитей?"это как пытаться исправить заторы на дорогах, спрашивая"в этом городе слишком много машин?" правильный вопрос, чтобы задать"почему в этом городе так много светофоров, где могут быть автострады?" проблема не в нити; проблема в том, что они ждут друг друга вместо того, чтобы ехать до места назначения без остановки.

должен ли я просто начать искать в другом области?

откуда нам знать?


Не зная структуры вашей программы, я могу только сказать вам, что синхронизация значит в отношении потоков. Я действительно не могу сказать вам, в чем проблема в вашей программе.

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

Если у вас есть string, например, что ваши два потока пишут, то, если у вас не было синхронизации потоков (т. е. с помощью AutoResetEvents или семафоров и т. д.), то один поток может быть в середине изменения строки каким-то образом, быть прерван ОС (возможно, это был срез времени), и теперь второй поток может начать чтение из этой строки, которая находится в неопределенном состоянии. Это будет иметь с вашей программой, так что для того, чтобы избегайте таких вещей и многих других возможных ошибок, которые могут быть вызваны потоковой передачей, вы синхронизация доступ к общему ресурсу, путем замок это, так что только один поток за раз может писать / читать в/из него, и любой другой поток, желающий сделать это пока другой делает это, должен ждать, пока наш первый поток отпустил замок, который он держит.

это, в очень упрощенном объяснении, что означает синхронизация потоков и для чего он нужен.
Есть еще много вещей, которые приходят с потоками, но это тема всей книги.

Что касается вашего "состояния синхронизации потоков", я бы предположил, что это означает, что многие потоки тратят свое время на ожидание других потоков, которые содержат некоторый общий ресурс.

по сути это означает, что ваша программа действительно не работает по совместительству, но и делает вещи в серийный, как потоки тратить свое время ожидание других потоков. Это означает, что программа написана не очень хорошо, чтобы действительно работать одновременно, что, кстати, не обязательно является легким подвигом для достижения,в зависимости от ситуации, это очевидно.

надеюсь, что это помогает.


есть несколько способов синхронизации потоков может убить производительность:

  1. инструкции по реализации синхронизации требуют времени для выполнения, особенно для подпрограмм синхронизации, требующих перехода в режим ядра, такой как поток.Сон.)( В худшем случае одинокий поток, часто вызывающий подпрограммы синхронизации, вводит много накладных расходов без реальной выгоды.
  2. когда больше чем одному потоку нужен исключительный доступ к ресурсов в то же время, по крайней мере, один поток застревает в ожидании. Наихудший сценарий здесь заключается в том, что есть какой-то центральный ресурс, к которому каждый должен часто обращаться. В этом случае многопоточный код подвергается серьезной опасности стать дорогостоящим, медленным и сложным способом работы одного потока за раз.

Что касается того, сколько слишком много: синхронизация-это то, что требует времени, но на самом деле не делает никакой полезной работы. Поэтому из спектакля перспектива идеальное количество синхронизации всегда равно нулю. Следовательно, высокое значение, которое помещается на архитектуры shared-nothing и неизменяемые структуры данных. Оба метода помогают организовать код таким образом, чтобы исключить или уменьшить необходимость синхронизации.

конечно, мир не идеален, поэтому некоторая синхронизация обычно неизбежна. Но даже тогда это должно быть сделано с использованием самых легких конструкций. Например, не используйте оператор lock, когда метод в сблокированного будет делать. Или уменьшите частоту, с которой это должно произойти, разработав потоки для отправки рабочего продукта в центральную структуру данных пакетами, а не делая много высокочастотных обновлений.