Author |
Message |
Written on: 13. 10. 2015 [17:15]
|
zeronineseven
Владимир Лебедев
Topic creator
registered since: 07.10.2015
Posts: 6
|
Здравствуйте!
Разрабатываю собственный DAQ-модуль на основе шаблона "src/moduls/daq/=Tmpl=" под OpenSCADA 0.9(svn ревизия 2298) на Debian с ядром 3.16.0-4-amd64 и компилятором g++ 4.9.2-10.
Основная часть работы каждого контроллера в модуле происходит в отдельном потоке(аналогично модулям ModBus и SNMP). Проблема в том, что я никак не могу понять, как из этого потока безопасно обновить значения атрибутов без потенциальной возможности возникновения race condition т.е., насколько я понимаю, делать что-то в духе
...
for(unsigned i_p = 0; i_p < cntr.p_hd.size() && !cntr.redntUse(); i_p++) {
try {
cntr.p_hd[i_p].at().vlAt("Test").at().setS("test_value", 1, true);
} catch(TError err) {
mess_err(err.cat.c_str(), "%s", err.mess.c_str());
}
}
...
в TMdContr::Task(реализованного аналогично тому, что есть в шаблоне) принципиально не безопасно т.к. TVal::setS(и другие подобные функции) не имеют встроенного механизма синхронизации. Каким образом тогда лучше организовать обновление значений атрибутов? Через синхронизированную очередь и читать/обновлять из неё значения в TParamContr::vlGet ил TTypeParam::vlGet? Или как-нибудь совсем по-другому?
И ещё один связанный вопрос: в шаблоне DAQ-модуля членом класса TMdContr является en_res типа Res - какова его функция?
Я чувствую, что упускаю что-то очевидное и буду благодарен за любую помощь. Заранее спасибо!
|
Written on: 13. 10. 2015 [23:33]
|
fido_max
Maxim Kochetkov
Contributor
registered since: 28.10.2010
Posts: 129
|
Синхронизацию можно делать через мьютекс:
в классе: pthread_mutex_t res
в конструкторе:
pthread_mutexattr_t attrM;
pthread_mutexattr_init(&attrM);
pthread_mutexattr_settype(&attrM, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&res, &attrM);
pthread_mutexattr_destroy(&attrM);
там где надо заблокировать код:
MtxAlloc prmRes(res, true);
разблокировать:
либо prmRes сам умрет как локальная переменная вне зоны видимости.
В деструкторе удалить:
pthread_mutex_destroy(&res);
Подробности реализации можно найти в любом модуле.
|
Written on: 14. 10. 2015 [10:20]
|
roman
Roman Savochenko
Moderator Contributor Developer
registered since: 12.12.2007
Posts: 3750
|
"zeronineseven" wrote:
...
в TMdContr::Task(реализованного аналогично тому, что есть в шаблоне) принципиально не безопасно т.к. TVal::setS(и другие подобные функции) не имеют встроенного механизма синхронизации.
С чего Вы взяли? Имеет:
pthread_mutex_lock(&dataRes());
string pvl = *val.s;
val.s->assign(value.data(), value.size());
pthread_mutex_unlock(&dataRes());
"zeronineseven" wrote:
И ещё один связанный вопрос: в шаблоне DAQ-модуля членом класса TMdContr является en_res типа Res - какова его функция?
Исключение одновременного доступа (thread-safe) к перечню ссылок, включенных в обработку параметров.
Learn, learn and learn better than work, work and work.
|
Written on: 21. 10. 2015 [14:53]
|
zeronineseven
Владимир Лебедев
Topic creator
registered since: 07.10.2015
Posts: 6
|
Большое спасибо за ответы, roman и fido_max!
"roman" wrote:
С чего Вы взяли? Имеет:
pthread_mutex_lock(&dataRes());
string pvl = *val.s;
val.s->assign(value.data(), value.size());
pthread_mutex_unlock(&dataRes());
Да, вы совершенно правы - TVal::setS и TVal::setO имеют внутри синхронизацию, но тогда почему в остальных функциях установки значения она отсутствует (TVal::setI, TVal::setR, TVal::setB)? Если я правильно понимаю, то они не могут быть атомарными сами по себе и здесь может возникнуть race condition в случае, когда, например, TVal::setI будет вызван из Task-потока котроллера, а TVal::getI - из потока, который считывает значения для вывода в GUI. Или я что-то проглядел?
|
Written on: 21. 10. 2015 [15:07]
|
roman
Roman Savochenko
Moderator Contributor Developer
registered since: 12.12.2007
Posts: 3750
|
"zeronineseven" wrote:
Да, вы совершенно правы - TVal::setS и TVal::setO имеют внутри синхронизацию, но тогда почему в остальных функциях установки значения она отсутствует (TVal::setI, TVal::setR, TVal::setB)?
Потому-что остальное это скаляры, с которыми проблем доступа быть не может в принципе.
Единственно, что в случае с целым и вещественным теоретически может быть прочитано промежуточное значение, т.е. когда, например, в ячейку записалось четыре байта из восьми и другой поток такое прочитал, т.е. атомарность. Но это никогда проблем стабильности не создавало, и максимум к чему может привести это к промежуточному значению, которое напоминает сглажывание, что очевидно не проблема и платить цену за это производительностью лишнее.
Опять-же у меня с этим нигде проблем нет, как следствие это не проблема!
Learn, learn and learn better than work, work and work.
|
Written on: 21. 10. 2015 [18:01]
|
zeronineseven
Владимир Лебедев
Topic creator
registered since: 07.10.2015
Posts: 6
|
Огромное спасибо за разъяснения, roman!
|
Written on: 21. 10. 2015 [19:29]
|
fido_max
Maxim Kochetkov
Contributor
registered since: 28.10.2010
Posts: 129
|
А зачем может понадобиться создавать несколько потоков на контроллер?
Даже если это и так, то зачем они читают/пишут одни и те же атрибуты?
В этом случае можно взводить семафор в начале процедуры записи и отпускать сразу после завершения записи.
|
Written on: 05. 11. 2015 [13:44]
|
zeronineseven
Владимир Лебедев
Topic creator
registered since: 07.10.2015
Posts: 6
|
"fido_max" wrote:
А зачем может понадобиться создавать несколько потоков на контроллер?
Даже если это и так, то зачем они читают/пишут одни и те же атрибуты?
Я имел в виду, что помимо тех потоков, которые могут быть созданы внутри модуля, так же существует ещё и поток GUI(во всяком случае для Vision; WebVision ещё не смотрел, но почти уверен, что тоже), в который синхронизацию через собственные мюьтексы без правки исходников этого самого GUI-потока засунуть не получится т.к. OpenSCADA не умеет "спрашивать" контроллер/модуль/параметр о том, на каких мьютексах нужно синхронизироваться. Ну или я всё неправильно понял :D
|