tl;dr:
class Controller
{
public:
volatile Netconsole* nc;
void init(); //initialize the threads
void calculate(); // handler for the "mothership app"
void senderThreadLoop(); //also calls reinitNet() if connection is broken.
void listenerThreadLoop();
inline void reinitNet(){ delete nc; nc = new Netconsole(); }
}
// вътре в Json::Value header = nc->Recv();
error: passing 'volatile Netconsole' as 'this' argument discards qualifiers [-fpermissive]
Указателят към екземпляр на помощен клас (Netconsole), споделен между две нишки, трябва да бъде актуализиран и в двете нишки, ако помощният клас е инстанциран отново, но декларирането му като непостоянен генерира горната грешка. Ако се актуализира само в една нишка, другата нишка все още може да използва стар, невалиден указател. Как да се гарантира, че е актуализиран и в двете, но използването на методи през показалеца не задейства горната грешка?
Разширена информация:
Библиотеката "smart glue logic", която пиша, се използва за предаване и конвертиране на съобщения между софтуер на трета страна и персонализирано устройство. Състои се от три основни нишки:
- манипулатор: основната нишка на приложението на трета страна периодично извиква функция „изчисляване“ в моята библиотека, за да обработва нови актуализации – данни за изпращане, получени данни
- подаваща нишка, която преобразува и изпраща всичко, което манипулаторът е избутал в изпращащия буфер
- поток слушател, който преобразува и изпраща всички данни, получени от устройството, в буфер за получаване.
Както подателят, така и нишките слушател използват един и същ клас помощни програми, който управлява мрежовата комуникация с устройството; при инициализация класът създава връзка с устройството и двете нишки изпълняват блокиращи четения или съответно чакат нови данни за изпращане. В случай на проблеми изпращащата нишка извършва цялата работа по „поддръжка“, докато слушащата нишка влиза в безопасно състояние в очакване на връщане на връзката.
Сега, тъй като двете нишки споделят една връзка с устройството, и двете споделят едно и също копие на комуникационния клас като указател към този клас.
Проблемът е в процедурата за повторно свързване - тя включва унищожаване и създаване на екземпляр на помощен клас, използвайки безопасно изключване и инициализация, които вече присъстват в деструктора и конструктора. В резултат на това указателят се променя. Без volatile
е много вероятно слушателят да не получи актуализирания указател. С volatile той протестира - ненужно, защото nc
(указателят) няма да се промени в произволен момент - първо слушателят се уведомява за проблем, след това влиза в безопасно състояние, в което не извършва никакви операции върху 'nc' и уведомява изпращача, че е готов. Едва тогава подателят извършва поправката и уведомява слушателя да възобнови нормалната работа.
И така, какво е правилното решение в тази ситуация?
std::atomic
- person NathanOliver   schedule 14.09.2015volatile
не е създаден за безопасност на нишки (и няма да го постигне). Използвайтеstd::mutex
, за да го защитите от условия на състезание. - person πάντα ῥεῖ   schedule 14.09.2015Netconsole* volatile nc;
, за да се постигне заявената цел, дори акоvolatile
би означавало безопасен за нишки. - person MSalters   schedule 14.09.2015int x=0; thread1(){ myMutex.lock(); x = 1 ; myMutex.unlock(); } thread2(){ int y=0; myMutex.lock(); y = x ; myMutex.unlock(); cout << y <<endl; }
- абсолютно не е гарантирано, че ще отпечатате1
, тъй като старата стойност на x в thread2 може да бъде изтеглена от регистър/кеш, освен акоx
не е обявено за непостоянно. - person SF.   schedule 14.09.2015std::atomic
е полезен, защото неговият.load
метод ви позволява да укажете бариера на паметта, независима от мютекс. - person MSalters   schedule 14.09.2015memcpy
реализации). Освен това Intel го гарантира само до 8 байта IIRC. - person MSalters   schedule 14.09.2015