|
Вопрос # 3 757/ вопрос открыт / |
|
Здравствуйте!
в основном приложении у меня есть экземпляр класса TOPCclient
в этом классе я создаю поток TOPCmonitorThread.
этот поток в Execute постоянно работает со свои public членом WinCCstateList. периодически я хочу получать в основном приложении данные из этого WinCCstateList. для этого я обращаюсь через функцию WinCCgetClientState класса TOPCclient к public члену WinCCstateList потока. (часть кодов в приложении)
Проблема в том что периодически программа обязательно зависает! и мне кажется что дело тут в синхронизации. Моих monitoringThread.Suspend; и monitoringThread.Resume; на время обращения к public члену потока WinCCstateList нехватает.
что вы можете посоветовать чтобы проверить или исправить данную ситуацию?
Приложение: Переключить в обычный режим- type
- TOPCclient = class
- private
-
- monitoringThread: TOPCmonitorThread;
- callbackAddr: pCallbackWinCCDataChanged;
-
- iniFilePath: string;
- IniFileHandler: TIniFile;
- .......
- public
- function Init(fileIniPath: string; callback: pCallbackWinCCDataChanged): boolean;
- procedure Free;
- procedure WinCCparametrChangedCallback(parametr: string; value: integer);
- function WinCCgetClientState(WinCCTag1, WinCCTag2: string): variant;
- ..........
- end;
-
- type
- TOPCmonitorThread = class(TThread)
- private
- callbackAddr: pCallbackWinCCDataChanged;
- procedure HandleInput;
- .....
- protected
- procedure InitThread(callbac: pCallbackWinCCDataChanged);
- procedure Execute; override;
- public
- WinCCstateList: TStringList;
- .......
- end;
-
- function TOPCclient.WinCCgetClientState(WinCCTag1, WinCCTag2: string): variant;
- var
- .....
- begin
-
-
- for i:=1 to 3 do
- begin
- for j:=0 to monitoringThread.WinCCstateList.Count-1 do
- begin
- paramStr := monitoringThread.WinCCstateList[j];
- if ((paramStr <> '') and (Pos('---', paramStr) <= 0)) then
- begin
- delimiterPos := Pos(' ', paramStr);
- if (delimiterPos > 0) then
- begin
- comand := Copy(paramStr, 1, delimiterPos-1);
- tmpStr := Copy(paramStr, delimiterPos+1, Length(paramStr)-delimiterPos);
- oldData := StrToInt(tmpStr);
-
- if ((Pos(WinCCTag1, comand) > 0) and (Pos(WinCCTag2, comand) > 0)) then
- begin
- callbackAddr(comand, oldData);
- end;
- end;
- end;
- end;
- end;
-
-
- end;
 |
Вопрос задал: AlexMPEI (статус: 1-ый класс)
Вопрос отправлен: 11 февраля 2010, 13:43
Состояние вопроса: открыт, ответов: 1.
|
Ответ #1. Отвечает эксперт: Вадим К
Здравствуйте, AlexMPEI!
А кто Вас научил таким образом синхронизировать потоки? Дело в том, что suspend, будучи вызванный с другого потока, останавливает поток абсолютно в произвольной точке. Абсолютно. Поэтому поля, которые Вы читаете с остановившегося потока могут быть несогласованными. И фраза "в любой точке" не значит в "любой строке". А если ещё и модифицировать их с основного потока...
Дальше.
Зачем останавливать поток, что бы распарсить параметры командной строки - я не понимаю. их вообще надо при старте парсить один раз.
Как же сделать корректную модификацию полей потока? очень просто - применяем критические секции или мютексы/семафоры. С критическими секциями наиболее просто.
Объявим у потока публичное поле типа lock:TCriticalSection;
при инициализации потока создадим его.
Lock:=TCriticalSection.Create;
в деструкторе - удаляем - Lock.free;
Тепеть, для кода, который не может выполняться паралельно, заключаем в
Lock.Enter;
код....
Lock.Leave;
Понятно, что основной тред должен вызывать публичное поле потока, а не создавать свою критическую секцию.
Особенность этой конструкции в том, что в данный участок кода может зайти только один тред. А все остальные будут ждать. Логично, что если основной поток хочет использовать, то все куски кода в критической секции должны отрабатывать максимально быстро.
Учитывая, что может происходить исключение, а незакрытая критическая секция приведет зависанию, то хорошо использовать конструкцию вида
Lock.Enter;
try
//код
finally
Lock.Leave;
end;
Если будет ругаться, что неизвестный идентификатор - добавляем syncobjs в uses.
И на закуску - читаем здесь http://www.interface.ru/home.asp?artId=6105
 |
Ответ отправил: Вадим К (статус: Академик)
Время отправки: 11 февраля 2010, 14:09
|
Мини-форум вопроса
Всего сообщений: 4; последнее сообщение — 11 февраля 2010, 15:21; участников в обсуждении: 2.
|
AlexMPEI (статус: 1-ый класс), 11 февраля 2010, 14:27 [#1]:
в моем случае я из основного треда только читаю паблик лист потока, а в потоке и читаю и пишу. причем в моих тестах, при которых рано или поздно приложение зависает, поток тоже только читает и ничего не пишет. Могу я в этом случае получать блокировку даже при усыплении потока?
Т.к. я не пишу в паблик лист из основного треда я и не стал заморачиваться с синхронизацией, т.к. решил что чтению это не мешает при том что я еще и усыпляю поток, чтобы он не использовал лист в это время.
там парсятся не параметры командной строки. Там в листе перебирается все содержимое и ищутся строки, содержащие переданные теги и соответствующие им значения. Периодически приходят запросы на получение текущего состояния тегов - я их и читаю из листа, а отслеживанием их изменения во внешней среде и обновлением их в листе занимается поток.
|
|
Вадим К (статус: Академик), 11 февраля 2010, 14:38 [#2]:
Цитата (AlexMPEI):
Т.к. я не пишу в паблик лист из основного треда я и не стал заморачиваться с синхронизацией
Не стоит так думать. Одно дело, когда читается атомарный тип - integer или подобное. А так - нельзя.
Цитата (AlexMPEI):
т.к. решил что чтению это не мешает при том что я еще и усыпляю поток, чтобы он не использовал лист в это время.
А вот это абсолютно неправильно. поток лучше усыплять только самим собой. то есть, поток должен усыплять себя сам. Иначе ничего не гарантируется.
Цитата (AlexMPEI):
там парсятся не параметры командной строки.
тогда Вы забили одно с важных правил программирования - не использовать системные переменые в качестве своих. как к примеру paramStr. Обычно либо компилятор сразу ругает, либо всё хорошо. но иногда бывает...
Галочка "подтверждения прочтения" - вселенское зло.
|
|
AlexMPEI (статус: 1-ый класс), 11 февраля 2010, 14:48 [#3]:
значит в моем случае скорее всего будет достаточно заключить все обращения к паблик листу в Execute потока в конструкции
Lock.Enter;
try
//код
finally
Lock.Leave;
end;
?
|
|
Вадим К (статус: Академик), 11 февраля 2010, 15:21 [#4]:
не только. а и в основном потоке тоже. Но надо не просто один вызов, а некоторые целостные конструкции.
То есть, скорее всего в основном потоке будет всё приведенная выше процедура, а в рабочем потоке - надо смотреть. так на глаз сложно что советовать. Слишком много возьмете - основной поток будет подтормаживать - он же будет ждать, пока рабочий поток выйдет с критической секции, меньше - тоже плохо.
Я бы делал так. у рабочего треда есть специальный "временный объект-свойство". Основной поток заполняет его и в критической секции выставляет переменную флаг.
рабочий поток время от времени проверяет этот же флаг и если он выставленный - то копирует с временного объекта себе, и сбрасывает переменную. понятно, что оба эти куска должны быть завернуты в критическую секцию.
справитесь с кодом?
Галочка "подтверждения прочтения" - вселенское зло.
|
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|