Почему Thread.Sleep (0) исправляет мои проблемы и как их избежать? - PullRequest
9 голосов
/ 19 февраля 2009

Я работаю над клиент-серверным приложением. В определенные моменты времени на определенных машинах, когда более 5 клиентов запрашивают данные, кажется, что они зашли в тупик. Если я вмешаюсь, чтобы решить проблему, то программа, кажется, обрабатывает. Просто установите точку останова, когда я знаю, что программа выполняется, и если она несколько раз достигнет точки останова, это приведет к ее завершению. Если я вставлю Thread.Sleep (0) в определенные моменты кода, в основном вокруг некоторых интенсивных циклов ЦП, это, по-видимому, в значительной степени решит проблему. Единственная проблема, которая у меня возникла сейчас, заключается в том, что если я вызову Thread.Sleep (0) слишком много, это может замедлить код. Если я не называю это достаточно, код, кажется, в тупике. Хотя я могу убедиться, что он не находится в тупике, потому что, если я войду в код, это приведет к исчезновению проблемы просто потому, что я приостанавливаю поток.

Есть ли хороший способ отследить, что именно вызывает это. Кажется, что это происходит только на моем ноутбуке с Windows Vista, но не на моем компьютере с Windows XP. Тем не менее, отладка невозможна, потому что простое вмешательство в код приводит к устранению проблемы. Я читал комментарии о том, что вызов Thread.Sleep (0) является плохой практикой и не должен быть необходим, и мне не нравится вставлять код типа колдовства в мои приложения, и я не понимаю, почему он должен быть там , Любые указатели будут с благодарностью.

[EDIT] Я также могу убедиться, что код все еще работает, когда он «заблокирован», потому что, если я оставлю его достаточно долго, он закончится, только количество времени, которое потребуется, будет на много порядков выше. Я имею в виду, что на самом деле это по крайней мере в 100 раз медленнее, когда он находится в этом «заблокированном» режиме. ЦП привязан на 80-95%, поэтому он работает, хотя то, что он делает, мне не подходит, потому что на выполнение задачи уходит вечность.

[Подробнее] Просто потому, что все здесь настаивают на том, что это тупик, я удалил весь код, который делал любую блокировку. Была только пара строк кода, которые делали какие-либо блокировки. Потоки по большей части работают совершенно независимо, поэтому совсем не сложно было полностью снять блокировку. Тем не менее проблема сохраняется. В моем коде больше нет синхронизирующих тактов, больше нет мьютексов, больше не видно того, что, как я вижу, могло бы привести к тупику, но проблема все еще существует. И это не тупик. Он работает, хотя и очень медленно, хотя и поглощает все ресурсы процессора.

Ответы [ 10 ]

11 голосов
/ 19 февраля 2009

Thread.Sleep (0) - доходность. Я предполагаю, что это меняет способ звонка, чтобы избежать некоторых проблем. Я думаю, что если вы выпустите код с выходом и запустите его на 1000 машин, вы получите много сообщений об ошибках. Я предполагаю, что вам нужен некоторый тип блокировки / критической секции, чтобы избежать вашей мертвой блокировки, потому что некоторые, где ваш код не является потокобезопасным Это может быть в библиотеке, которую вы звоните.

  1. Добавьте логирование и посмотрите, не исчезнут ли проблемы. Надеюсь, вы сможете выяснить, какие функции вызывают мертвую блокировку
  2. Добавьте несколько критических разделов. Использует подход «разделяй и властвуй», с помощью которого вы сможете сузить место возникновения проблемы.
4 голосов
/ 19 февраля 2009

На это невозможно ответить, не глядя на всю кодовую базу.

Вызов Thread.Sleep - это УЖАСНАЯ практика, и вы не должны этого делать. Вы в основном перемещаетесь по времени вашего кода, который приводит к взаимоблокировке, в отличие от фактического решения условия взаимоблокировки с самого начала.

Причина, по которой отладка не показывает проблемы, заключается в том, что когда отладчик останавливается, все потоки в вашем коде останавливаются, что также искажает время выполнения вашей программы.

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

3 голосов
/ 19 февраля 2009

Что внутри этих петель? Подобные проблемы могут возникнуть, если вы проверите какое-то поле в цикле для синхронизации нескольких потоков:

while (_field); // waiting for _field change in another thread

Это решение будет работать очень медленно, и вызовы Thread.Sleep (0) не являются решением, но в некоторых случаях могут быть взломаны. Это можно исправить, если вы измените этот цикл синхронизации с помощью вызова метода WaitHandle.WaitOne () какого-либо объекта синхронизации (например, ManualResetEvent ) и поместите сигнал для этого дескриптора в другой поток. Может быть, ваша проблема в этом? Пожалуйста, предоставьте некоторую часть вашего кода.

2 голосов
/ 19 февраля 2009

Сколько одновременных потоков работает, когда он останавливается? Если у вас слишком много потоков, процессор может тратить все свое время на переключение контекста. Кроме того, вы будете жевать 1+ МБ памяти на поток.

1 голос
/ 19 февраля 2009

Если аппаратное обеспечение не заставляет вас, вы никогда не должны использовать sleep ().

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

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

1 голос
/ 19 февраля 2009

У вас определенно есть проблемы с многопоточностью, которые необходимо устранить. Вызов thread.Sleep (0) вызывает включение планировщика. Это, вероятно, дает каждому потоку возможность работать достаточно, чтобы все заработало. Я не просто оставил бы там сон и оставил его на этом, потому что это тот тип вещей, который работает какое-то время, а затем совершенно несвязанные изменения заканчивают тем, что ломали вещи.

1 голос
/ 19 февраля 2009

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

Если ваш код не стоит таких больших усилий, тогда оставьте Thread.sleep () там, потому что это не сильно повредит вашей производительности в общей схеме вещей.

1 голос
/ 19 февраля 2009

Попробуйте печатать строку каждый раз, когда вы блокируете мьютекс, и печатать другую строку, когда вы ее разблокируете. Не забудьте указать имя функции и идентификатор потока при его печати. Должен дать вам представление о том, когда он блокируется. Похоже, что состояние гонки, вызывающее sleep (0), все еще заставляет ЦП использовать циклы, обрабатывающие вызов функции. Поэтому вызывая присущий сон.

0 голосов
/ 19 февраля 2009

Да, возможно, вы столкнулись с перегрузкой планировщика.

Я искренне надеюсь, что нет.

0 голосов
/ 19 февраля 2009

Может быть, попробуйте использовать инструмент вроде Typemock Racer ?

Отказ от ответственности: я никогда не использовал этот инструмент раньше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...