| 
| 
 | Вопрос # 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]:не только. а и в основном потоке тоже. Но надо не просто один вызов, а некоторые целостные конструкции. То есть, скорее всего в основном потоке будет всё приведенная выше процедура, а в рабочем потоке - надо смотреть. так на глаз сложно что советовать. Слишком много возьмете - основной поток будет подтормаживать - он же будет ждать, пока рабочий поток выйдет с критической секции, меньше - тоже плохо.
 
 Я бы делал так. у рабочего треда есть специальный "временный объект-свойство". Основной поток заполняет его и в критической секции выставляет переменную флаг.
 рабочий поток время от времени проверяет этот же флаг и если он выставленный - то копирует с временного объекта себе, и сбрасывает переменную. понятно, что оба эти куска должны быть завернуты в критическую секцию.
 справитесь с кодом?
 Галочка "подтверждения прочтения" - вселенское зло. |  Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте. |