У меня есть многопоточная программа с основным потоком, являющимся сторонним (не может изменить его) и чистым C. Моя задача состоит в том, чтобы создать вокруг него новые модули (в C ++), которые частично находятся в других потоках и должны использовать C Интерфейс программы. По сути, просто чтение некоторых переменных (целые числа, числа с плавающей запятой, ничего сложного), которые хранятся и обновляются в потоке C.
Теперь к моему вопросу: как я могу убедиться, что я не получаю мусор из интерфейса C при доступе к этим переменным, поскольку я не могу использовать мьютекс для блокировки его во время чтения. Это вообще возможно? Или в любом случае запись float / int является атомарной операцией?
Ты не можешь. Чтение и запись чего-либо не является атомарной операцией, и если вы не можете изменить код C, вам не повезло. Синхронизация всегда нужна и то и другое части должны быть синхронизированы.
Лучше всего попросить третью сторону обеспечить безопасность своей части и / или поделиться с вами механизмом блокировки.
Такие выражения, как «запись float / int [в любом случае] является атомарной операцией», к сожалению, недостаточно хорошо определены в C или C ++ (хотя с использованием std::atomic
в C ++ 11 и методы stdatomic.h из C11 могут помочь здесь — но это не поможет вам с взаимодействием C для библиотеки, которую вы не можете изменить, так что вы, вероятно, можете игнорировать ее здесь).
Вы можете найти руководство по этим вопросам на конкретных компиляторах и платформах — например, вы можете выяснить, что на большинстве платформ выровненные 32-битные или 64-битные операции чтения или записи будут атомарными, если выровнены, и что большинство компиляторов будут выровнены их соответственно.
Однако по этому пути лежит безумие. Если у вас несколько потоков, просто используйте функциональность POSIX / pthreads, например pthreads мьютексы — которые легко доступны как из C, так и из C ++, для защиты любого доступа к состоянию, разделяемому между потоками.
Поскольку вы не можете изменить код C, вам, возможно, придется сделать всю блокировку в коде C ++ до любого вызова библиотеки C, после разблокировки. Если вы можете читать, но не изменять код C, или документ очень четко описывает модель потоков / совместного использования, вы можете использовать стратегию тонкозернистой блокировки, но при отсутствии какого-либо профилирования, указывающего на узкое место, я ‘ Начнем с одной глобальной блокировки, которую вы используете для защиты каждого доступа к C API.
Ты не можешь Единственный правильный способ работы в этом сценарии — это работать только с аргументами, которые предоставляются вашим функциям вызывающим потоком C, и не сохранять никаких ссылок на них впоследствии. Там нет никакого способа гарантировать, что любые переменные не будут изменены — в общем случае.
Вы должны переосмыслить свою архитектуру, чтобы такая необходимость не возникала.
Если вы не можете убедиться, что код, который устанавливает значения переменных, синхронизирован, установка блокировки во время чтения не имеет смысла и не будет работать. Это не только атомарность операций, но и видимость данных — обновления этих переменных могут быть не видны другим потокам.
Если вы управляете основным потоком, вы должны создать новую переменную для каждого из тех, к которым у вас есть доступ, получить к ней доступ из основного потока и, используя блокировки, установить значение вновь созданной переменной. Затем из других потоков обращайтесь только к этим синхронизированным переменным.
int myVal = 0;
int main() {
while(!shouldQuit()) {
doSomeIndependentStuff();
pthread_lock(&mutex);
myVal = independentGlobalVal;
pthread_unlock(&mutex);
}
}
int getMyVal() {
int retVal = 0;
pthread_lock(&mutex);
retVal = myVal;
pthread_unlock(&mutex);
return retval;
}