лок на коллекцию
От: Аноним  
Дата: 09.03.07 13:51
Оценка:
Я в одном потоке делаю лок на коллекцию, тем не менее какой-то другой поток все равно влазиет и что-то там модифицирует.
Почему лок не работает?
Re: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 14:03
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Я в одном потоке делаю лок на коллекцию, тем не менее какой-то другой поток все равно влазиет и что-то там модифицирует.

А>Почему лок не работает?

потому что "какой-то другой поток" не использует 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) работает только для твоего потока, т.к. в твоем потоке он используется


Что-то я не понял... Разве лок не устанавливает эксклюзивный досуп к объекту?
Выходит, что я должен во всех местах, где я доступ делаю, лок писать, что ли?
Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.
Re[2]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 14:09
Оценка:
если возможности добавить lock в "какой-то другой поток" нет, то можешь использовать Synchronized врапер (если не используется интерфейс коллекции IEnumerable!):

вместо:
    ArrayList myArrayList = new ArrayList();
    
    private void doWork()
    {
        lock(myArrayList.SyncRoot)
            myArrayList.Add(new object());
    }


используй:
    ArrayList myArrayList = ArrayList.Synchronized( new ArrayList() );
    
    private void doWork()
    {
        myArrayList.Add(new object());
    }


Важно помнить что foreach для Synchronized объектов НЕ потокобезопасен, поэтому использовать его нельзя, остальные операции Synchronized объектов потокобезопасны.
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[3]: лок на коллекцию
От: al Россия  
Дата: 09.03.07 14:15
Оценка: +1 :)
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, _Morpheus_, Вы писали:


А>Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.


lock — это и есть такой флажок. Но если ты будешь сам писать такой фложок — то тебе понадобиться lock для этого флоажка.


Re[3]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 14:21
Оценка:
Здравствуйте, <Аноним>, Вы писали:

_M_>>потому что "какой-то другой поток" не использует lock(myArrayList.SyncRoot), соответственно lock(myArrayList.SyncRoot) работает только для твоего потока, т.к. в твоем потоке он используется


А>Что-то я не понял... Разве лок не устанавливает эксклюзивный досуп к объекту?


устанавливает, но только внутри lock'а. Другие потоки lock не делают поэтому им по барабану что какойто решил пользоваться lock'ом. Тут вопрос такой — если используешь lock, то используй его везде где есть опасность что другой поток может паралельно использовать ресурс.

А>Выходит, что я должен во всех местах, где я доступ делаю, лок писать, что ли?


совершенно верно

А>Нафик тогда этот самый лок нужен? Я могу просто флажок выставлять, мол "коллекция занята, подождите", а другие потоки будут в цикле крутиться и ждать, пока его снимут.


тут просто непонимание основ многопоточности, т.к.:
1. Если "другие потоки будут в цикле крутиться и ждать, пока его снимут", то они будут ничего не делая загружать процессор на 100%, не давая рабочим потокам выполнить свою работу.
2. Ты не сможешь гарантированно проверить и установить флаг, например:
    ArrayList myArrayList = new ArrayList();
    bool myArrayListBusy = false
    void threadProc()
    {
        if(!myArrayListBusy)
        {
            myArrayListBusy = true;
            myArrayList.Add(new object());
            myArrayListBusy = false;
        }
    }

рассмотрим как он будет выполнятся:
поток 1: проверяет флаг, флаг показывает что объект не занят
поток 2: проверяет флаг, флаг показывает что объект не занят
поток 1: устанавливает флаг чтобы он указывал что объект занят
поток 2: устанавливает флаг чтобы он указывал что объект занят
поток 1: модифицирует объект думая что другие к нему в это время не обращаются
поток 2: одновременно с потоком 1 модифицирует объект думая что другие к нему в это время не обращаются

таким образом флаг тебе не поможет
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[4]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 14:28
Оценка:
забыл добавить, про "крутиться и ждать"...

Если поток будет крутится и ждать:
    ArrayList myArrayList = new ArrayList();
    bool myArrayListBusy = false
    void threadProc()
    {
        while(myArrayListBusy);

        myArrayListBusy = true;
        myArrayList.Add(new object());
        myArrayListBusy = false;
    }


то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[5]: лок на коллекцию
От: Dikaaa Россия  
Дата: 09.03.07 14:50
Оценка: :)
Здравствуйте, _Morpheus_, Вы писали:

_M_>забыл добавить, про "крутиться и ждать"...

_M_>то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!

Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);
------------------------------------
Не бывает неудач, есть только опыт.
Re[6]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 15:37
Оценка:
Здравствуйте, Dikaaa, Вы писали:

_M_>>забыл добавить, про "крутиться и ждать"...

_M_>>то это очень серьезная ошибка, т.к. поток выполняя while(myArrayListBusy); будет отжирать огромную кучу процессорного времени, ничего полезного при этом не делая, но катастрофически тормозя всю систему и мешая другим программам и потокам выполнять полезную работу. Поэтому так делать нельзя!

D>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);


нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[7]: лок на коллекцию
От: Dikaaa Россия  
Дата: 09.03.07 15:44
Оценка:
D>>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);

_M_>нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.


Мы сейчас говорим о неприменимости Thread.Sleep вообще, или в данном случае?
Если вообще, можно продолжить разговор. Если в даном случае, то я с Вами согласен.
------------------------------------
Не бывает неудач, есть только опыт.
Re[8]: лок на коллекцию
От: Dikaaa Россия  
Дата: 09.03.07 15:46
Оценка:
Разговор про Thread.Sleep в цикле разумеется.
------------------------------------
Не бывает неудач, есть только опыт.
Re[8]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 15:49
Оценка:
Здравствуйте, Dikaaa, Вы писали:

D>>>Можно, если внутри цикла поставить Thread.Sleep(Максимально разумное число для данного алгоритма);


_M_>>нельзя, Thread.Sleep будет выступать костылем, просто снижая загрузку процессора, но не минимизируя ее.


D>Мы сейчас говорим о неприменимости Thread.Sleep вообще, или в данном случае?

D>Если вообще, можно продолжить разговор. Если в даном случае, то я с Вами согласен.

Возможность применить Thread.Sleep есть, но так делать нельзя. Нельзя это не значит невозможно, это значит что не нужно так делать.
Если нужно дождаться какогото события, для этого есть Event'ы.
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[9]: лок на коллекцию
От: Dikaaa Россия  
Дата: 09.03.07 15:58
Оценка:
_M_>Возможность применить Thread.Sleep есть, но так делать нельзя. Нельзя это не значит невозможно, это значит что не нужно так делать.
_M_>Если нужно дождаться какогото события, для этого есть Event'ы.

Есть визуальный интерфейс, подписываемся на события.
Есть поток, в котором реализован конечный автомат.
Состояние этого автомата меняется из обработчиков событий
Поток нужен, что-бы форма не зависала, во время того, как происходит действие на событие.
Так вот в данном случае ИМХО Thread.Sleep допустим 30 на каждой итерации цикла в потоке я считаю вполне оправданным.
Поскольку нагрузка на процессор с 30ms опускается до нуля. А такая скорость реакции меня вполне устраивает.

Есть вариант реализовать это по другому?
------------------------------------
Не бывает неудач, есть только опыт.
Re[10]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 16:31
Оценка:
Здравствуйте, 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();
    }
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[11]: лок на коллекцию
От: _Morpheus_  
Дата: 09.03.07 16:32
Оценка:
тут читать так:

_M_>
_M_>                switch(newState)    // обработка нового состояния
_M_>
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[11]: лок на коллекцию
От: Dikaaa Россия  
Дата: 09.03.07 17:06
Оценка:
_M_>ущербное решение, чревато exception'ами и другими гадостями,
Почему чревато exception-ами?
да и нагрузка на процессор до нуля опуститься не может, был случай когда процесс с realtime приоритетом кушал процессор не по децки, а таск менагер показывал 0%..10%
Для процессора — 30мс это огромное время. Нагрузка падает до нуля с гораздо меньшим значением.

D>>Есть вариант реализовать это по другому?


Спасибо за код. Про AutoResetEvent не знал. Хорошая штука.
------------------------------------
Не бывает неудач, есть только опыт.
Re[12]: лок на коллекцию
От: Lloyd Россия  
Дата: 09.03.07 17:09
Оценка: +1
Здравствуйте, Dikaaa, Вы писали:

D>Спасибо за код. Про AutoResetEvent не знал. Хорошая штука.


Похоже кто-то сейчас переизобретет lock.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.