Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 3 757

/ вопрос открыт /

Здравствуйте!
в основном приложении у меня есть экземпляр класса TOPCclient
в этом классе я создаю поток TOPCmonitorThread.
этот поток в Execute постоянно работает со свои public членом WinCCstateList. периодически я хочу получать в основном приложении данные из этого WinCCstateList. для этого я обращаюсь через функцию WinCCgetClientState класса TOPCclient к public члену WinCCstateList потока. (часть кодов в приложении)
Проблема в том что периодически программа обязательно зависает! и мне кажется что дело тут в синхронизации. Моих monitoringThread.Suspend; и monitoringThread.Resume; на время обращения к public члену потока WinCCstateList нехватает.
что вы можете посоветовать чтобы проверить или исправить данную ситуацию?

Приложение:
  1. type
  2. TOPCclient = class
  3. private
  4.  
  5. monitoringThread: TOPCmonitorThread;
  6. callbackAddr: pCallbackWinCCDataChanged;
  7.  
  8. iniFilePath: string;
  9. IniFileHandler: TIniFile;
  10. .......
  11. public
  12. function Init(fileIniPath: string; callback: pCallbackWinCCDataChanged): boolean;
  13. procedure Free;
  14. procedure WinCCparametrChangedCallback(parametr: string; value: integer);
  15. function WinCCgetClientState(WinCCTag1, WinCCTag2: string): variant;
  16. ..........
  17. end;
  18.  
  19. type
  20. TOPCmonitorThread = class(TThread)
  21. private
  22. callbackAddr: pCallbackWinCCDataChanged;
  23. procedure HandleInput;
  24. .....
  25. protected
  26. procedure InitThread(callbac: pCallbackWinCCDataChanged);
  27. procedure Execute; override;
  28. public
  29. WinCCstateList: TStringList;
  30. .......
  31. end;
  32.  
  33. function TOPCclient.WinCCgetClientState(WinCCTag1, WinCCTag2: string): variant;
  34. var
  35. .....
  36. begin
  37.  
  38.  
  39. for i:=1 to 3 do
  40. begin
  41. for j:=0 to monitoringThread.WinCCstateList.Count-1 do
  42. begin
  43. paramStr := monitoringThread.WinCCstateList[j];
  44. if ((paramStr <> '') and (Pos('---', paramStr) <= 0)) then
  45. begin
  46. delimiterPos := Pos(' ', paramStr);
  47. if (delimiterPos > 0) then
  48. begin
  49. comand := Copy(paramStr, 1, delimiterPos-1);
  50. tmpStr := Copy(paramStr, delimiterPos+1, Length(paramStr)-delimiterPos);
  51. oldData := StrToInt(tmpStr);
  52.  
  53. if ((Pos(WinCCTag1, comand) > 0) and (Pos(WinCCTag2, comand) > 0)) then
  54. begin
  55. callbackAddr(comand, oldData);
  56. end;
  57. end;
  58. end;
  59. end;
  60. end;
  61.  
  62.  
  63. end;


AlexMPEI Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: 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

AlexMPEI (статус: 1-ый класс), 11 февраля 2010, 14:27 [#1]:

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

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

там парсятся не параметры командной строки. Там в листе перебирается все содержимое и ищутся строки, содержащие переданные теги и соответствующие им значения. Периодически приходят запросы на получение текущего состояния тегов - я их и читаю из листа, а отслеживанием их изменения во внешней среде и обновлением их в листе занимается поток.
Вадим К

Вадим К (статус: Академик), 11 февраля 2010, 14:38 [#2]:

Цитата (AlexMPEI):

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

Не стоит так думать. Одно дело, когда читается атомарный тип - integer или подобное. А так - нельзя.

Цитата (AlexMPEI):

т.к. решил что чтению это не мешает при том что я еще и усыпляю поток, чтобы он не использовал лист в это время.

А вот это абсолютно неправильно. поток лучше усыплять только самим собой. то есть, поток должен усыплять себя сам. Иначе ничего не гарантируется.

Цитата (AlexMPEI):

там парсятся не параметры командной строки.

тогда Вы забили одно с важных правил программирования - не использовать системные переменые в качестве своих. как к примеру paramStr. Обычно либо компилятор сразу ругает, либо всё хорошо. но иногда бывает...
Галочка "подтверждения прочтения" - вселенское зло.
AlexMPEI

AlexMPEI (статус: 1-ый класс), 11 февраля 2010, 14:48 [#3]:

значит в моем случае скорее всего будет достаточно заключить все обращения к паблик листу в Execute потока в конструкции
Lock.Enter;
try
//код
finally
Lock.Leave;
end;
?
Вадим К

Вадим К (статус: Академик), 11 февраля 2010, 15:21 [#4]:

не только. а и в основном потоке тоже. Но надо не просто один вызов, а некоторые целостные конструкции.
То есть, скорее всего в основном потоке будет всё приведенная выше процедура, а в рабочем потоке - надо смотреть. так на глаз сложно что советовать. Слишком много возьмете - основной поток будет подтормаживать - он же будет ждать, пока рабочий поток выйдет с критической секции, меньше - тоже плохо.

Я бы делал так. у рабочего треда есть специальный "временный объект-свойство". Основной поток заполняет его и в критической секции выставляет переменную флаг.
рабочий поток время от времени проверяет этот же флаг и если он выставленный - то копирует с временного объекта себе, и сбрасывает переменную. понятно, что оба эти куска должны быть завернуты в критическую секцию.
справитесь с кодом?
Галочка "подтверждения прочтения" - вселенское зло.

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:47
Выполнено за 0.02 сек.