Здравствуйте, <Аноним>, Вы писали:
А>Я в одном потоке делаю лок на коллекцию, тем не менее какой-то другой поток все равно влазиет и что-то там модифицирует. А>Почему лок не работает?
потому что "какой-то другой поток" не использует lock(myArrayList.SyncRoot), соответственно lock(myArrayList.SyncRoot) работает только для твоего потока, т.к. в твоем потоке он используется
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[2]: лок на коллекцию
От:
Аноним
Дата:
09.03.07 14:09
Оценка:
Здравствуйте, _Morpheus_, Вы писали:
А>>Я в одном потоке делаю лок на коллекцию, тем не менее какой-то другой поток все равно влазиет и что-то там модифицирует. А>>Почему лок не работает?
_M_>потому что "какой-то другой поток" не использует lock(myArrayList.SyncRoot), соответственно lock(myArrayList.SyncRoot) работает только для твоего потока, т.к. в твоем потоке он используется
Что-то я не понял... Разве лок не устанавливает эксклюзивный досуп к объекту?
Выходит, что я должен во всех местах, где я доступ делаю, лок писать, что ли?
Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.
если возможности добавить lock в "какой-то другой поток" нет, то можешь использовать Synchronized врапер (если не используется интерфейс коллекции IEnumerable!):
Важно помнить что foreach для Synchronized объектов НЕ потокобезопасен, поэтому использовать его нельзя, остальные операции Synchronized объектов потокобезопасны.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, _Morpheus_, Вы писали:
А>Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.
lock — это и есть такой флажок. Но если ты будешь сам писать такой фложок — то тебе понадобиться lock для этого флоажка.
Здравствуйте, <Аноним>, Вы писали:
_M_>>потому что "какой-то другой поток" не использует lock(myArrayList.SyncRoot), соответственно lock(myArrayList.SyncRoot) работает только для твоего потока, т.к. в твоем потоке он используется
А>Что-то я не понял... Разве лок не устанавливает эксклюзивный досуп к объекту?
устанавливает, но только внутри lock'а. Другие потоки lock не делают поэтому им по барабану что какойто решил пользоваться lock'ом. Тут вопрос такой — если используешь lock, то используй его везде где есть опасность что другой поток может паралельно использовать ресурс.
А>Выходит, что я должен во всех местах, где я доступ делаю, лок писать, что ли?
совершенно верно
А>Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.
тут просто непонимание основ многопоточности, т.к.:
1. Если "другие потоки будут в цикле крутиться и ждать, пока его снимут", то они будут ничего не делая загружать процессор на 100%, не давая рабочим потокам выполнить свою работу.
2. Ты не сможешь гарантированно проверить и установить флаг, например:
рассмотрим как он будет выполнятся:
поток 1: проверяет флаг, флаг показывает что объект не занят
поток 2: проверяет флаг, флаг показывает что объект не занят
поток 1: устанавливает флаг чтобы он указывал что объект занят
поток 2: устанавливает флаг чтобы он указывал что объект занят
поток 1: модифицирует объект думая что другие к нему в это время не обращаются
поток 2: одновременно с потоком 1 модифицирует объект думая что другие к нему в это время не обращаются
то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!
Здравствуйте, _Morpheus_, Вы писали:
_M_>забыл добавить, про "крутиться и ждать"... _M_>то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!
Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);
------------------------------------
Не бывает неудач, есть только опыт.
Здравствуйте, Dikaaa, Вы писали:
_M_>>забыл добавить, про "крутиться и ждать"... _M_>>то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!
D>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);
нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.
D>>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);
_M_>нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.
Мы сейчас говорим о неприменимости Thread.Sleep вообще, или в данном случае?
Если вообще, можно продолжить разговор. Если в даном случае, то я с Вами согласен.
------------------------------------
Не бывает неудач, есть только опыт.
Здравствуйте, Dikaaa, Вы писали:
D>>>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);
_M_>>нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.
D>Мы сейчас говорим о неприменимости Thread.Sleep вообще, или в данном случае? D>Если вообще, можно продолжить разговор. Если в даном случае, то я с Вами согласен.
Возможность применить Thread.Sleep есть, но так делать нельзя. Нельзя это не значит невозможно, это значит что не нужно так делать.
Если нужно дождаться какогото события, для этого есть Event'ы.
_M_>Возможность применить Thread.Sleep есть, но так делать нельзя. Нельзя это не значит невозможно, это значит что не нужно так делать. _M_>Если нужно дождаться какогото события, для этого есть Event'ы.
Есть визуальный интерфейс, подписываемся на события.
Есть поток, в котором реализован конечный автомат.
Состояние этого автомата меняется из обработчиков событий
Поток нужен, что-бы форма не зависала, во время того, как происходит действие на событие.
Так вот в данном случае ИМХО Thread.Sleep допустим 30 на каждой итерации цикла в потоке я считаю вполне оправданным.
Поскольку нагрузка на процессор с 30ms опускается до нуля. А такая скорость реакции меня вполне устраивает.
Есть вариант реализовать это по другому?
------------------------------------
Не бывает неудач, есть только опыт.
Здравствуйте, Dikaaa, Вы писали:
_M_>>Возможность применить Thread.Sleep есть, но так делать нельзя. Нельзя это не значит невозможно, это значит что не нужно так делать. _M_>>Если нужно дождаться какогото события, для этого есть Event'ы.
D>Есть визуальный интерфейс, подписываемся на события. D>Есть поток, в котором реализован конечный автомат. D>Состояние этого автомата меняется из обработчиков событий D>Поток нужен, что-бы форма не зависала, во время того, как происходит действие на событие. D>Так вот в данном случае ИМХО Thread.Sleep допустим 30 на каждой итерации цикла в потоке я считаю вполне оправданным. D>Поскольку нагрузка на процессор с 30ms опускается до нуля. А такая скорость реакции меня вполне устраивает.
ущербное решение, чревато exception'ами и другими гадостями, да и нагрузка на процессор до нуля опуститься не может, был случай когда процесс с realtime приоритетом кушал процессор не по децки, а таск менагер показывал 0%..10%
D>Есть вариант реализовать это по другому?
private AutoResetEvent _stateChangedEvent = new AutoResetEvent(false);
private Queue _stateQueue = new Queue();
private bool _shutDown = false;
private void threadProc()
{
int newState;
do
{
_stateChangedEvent.WaitOne();
while(!_shutDown)
{
lock(_stateQueue.SyncRoot)
{
if(_stateQueue.Count<1)
break;
newState = (int)_stateQueue.Dequeue();
}
switch(state) // обработка нового состояния
{
case 0:
//...
}
}
} while(!_shutDown);
}
private void stateChange_EventHandler(object sender, int newState)
{
lock(_stateQueue.SyncRoot)
_stateQueue.Enqueue((object)newState);
_stateChangedEvent.Set();
}
_M_>ущербное решение, чревато exception'ами и другими гадостями,
Почему чревато exception-ами?
да и нагрузка на процессор до нуля опуститься не может, был случай когда процесс с realtime приоритетом кушал процессор не по децки, а таск менагер показывал 0%..10%
Для процессора — 30мс это огромное время. Нагрузка падает до нуля с гораздо меньшим значением.
D>>Есть вариант реализовать это по другому?
Спасибо за код. Про AutoResetEvent не знал. Хорошая штука.
------------------------------------
Не бывает неудач, есть только опыт.