УкраїнськаEnglishmRussian
Вход/Новый
В теме нет новых постов

Потокобезопасное обновление атрибутов


Автор Сообщение
Сообщение создано: 13. 10. 2015 [17:15]
zeronineseven
Владимир Лебедев
Создатель темы
Зарегистрирован(а) с: 07.10.2015
Сообщения: 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 т.е., насколько я понимаю, делать что-то в духе
JAVASCRIPT
...
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 - какова его функция?

Я чувствую, что упускаю что-то очевидное и буду благодарен за любую помощь. Заранее спасибо!
Сообщение создано: 13. 10. 2015 [23:33]
fido_max
Maxim Kochetkov
Contributor
Зарегистрирован(а) с: 28.10.2010
Сообщения: 129
Синхронизацию можно делать через мьютекс:
в классе: pthread_mutex_t res
в конструкторе:
JAVASCRIPT
pthread_mutexattr_t attrM;
    pthread_mutexattr_init(&attrM);
    pthread_mutexattr_settype(&attrM, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&res, &attrM);
    pthread_mutexattr_destroy(&attrM);



там где надо заблокировать код:
JAVASCRIPT
MtxAlloc prmRes(res, true);

разблокировать:
JAVASCRIPT
prmRes.unlock();

либо prmRes сам умрет как локальная переменная вне зоны видимости.

В деструкторе удалить:
JAVASCRIPT
pthread_mutex_destroy(&res);

Подробности реализации можно найти в любом модуле.
Сообщение создано: 14. 10. 2015 [10:20]
roman
Roman Savochenko
Moderator
Contributor
Developer
Зарегистрирован(а) с: 12.12.2007
Сообщения: 3742
"zeronineseven" wrote:

...
в TMdContr::Task(реализованного аналогично тому, что есть в шаблоне) принципиально не безопасно т.к. TVal::setS(и другие подобные функции) не имеют встроенного механизма синхронизации.

С чего Вы взяли? Имеет:
JAVASCRIPT
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.
Сообщение создано: 21. 10. 2015 [14:53]
zeronineseven
Владимир Лебедев
Создатель темы
Зарегистрирован(а) с: 07.10.2015
Сообщения: 6
Большое спасибо за ответы, roman и fido_max!

"roman" wrote:

С чего Вы взяли? Имеет:
JAVASCRIPT
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. Или я что-то проглядел?
Сообщение создано: 21. 10. 2015 [15:07]
roman
Roman Savochenko
Moderator
Contributor
Developer
Зарегистрирован(а) с: 12.12.2007
Сообщения: 3742
"zeronineseven" wrote:

Да, вы совершенно правы - TVal::setS и TVal::setO имеют внутри синхронизацию, но тогда почему в остальных функциях установки значения она отсутствует (TVal::setI, TVal::setR, TVal::setB)?

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

Опять-же у меня с этим нигде проблем нет, как следствие это не проблема!

Learn, learn and learn better than work, work and work.
Сообщение создано: 21. 10. 2015 [18:01]
zeronineseven
Владимир Лебедев
Создатель темы
Зарегистрирован(а) с: 07.10.2015
Сообщения: 6
Огромное спасибо за разъяснения, roman!
Сообщение создано: 21. 10. 2015 [19:29]
fido_max
Maxim Kochetkov
Contributor
Зарегистрирован(а) с: 28.10.2010
Сообщения: 129
А зачем может понадобиться создавать несколько потоков на контроллер?
Даже если это и так, то зачем они читают/пишут одни и те же атрибуты?
В этом случае можно взводить семафор в начале процедуры записи и отпускать сразу после завершения записи.
Сообщение создано: 05. 11. 2015 [13:44]
zeronineseven
Владимир Лебедев
Создатель темы
Зарегистрирован(а) с: 07.10.2015
Сообщения: 6
"fido_max" wrote:

А зачем может понадобиться создавать несколько потоков на контроллер?
Даже если это и так, то зачем они читают/пишут одни и те же атрибуты?


Я имел в виду, что помимо тех потоков, которые могут быть созданы внутри модуля, так же существует ещё и поток GUI(во всяком случае для Vision; WebVision ещё не смотрел, но почти уверен, что тоже), в который синхронизацию через собственные мюьтексы без правки исходников этого самого GUI-потока засунуть не получится т.к. OpenSCADA не умеет "спрашивать" контроллер/модуль/параметр о том, на каких мьютексах нужно синхронизироваться. Ну или я всё неправильно понял :D



8444