У вас есть два основных варианта:
1) Вы указываете , в какой многопоточной среде ваша библиотека является поточно-ориентированной, и используете функции синхронизации этой среды.
2) Вы указываете, что ваша библиотека не поточно-ориентированная. Если ваш вызывающий объект хочет использовать его в многопоточной среде, то он несет ответственность за обеспечение его поточной безопасности за счет использования внешней синхронизации, если необходимо сериализовать все вызовы вашей библиотеки. Если ваша библиотека использует дескрипторы и не нуждается в каком-либо глобальном состоянии, это может означать, например, что если у них есть дескриптор, который они используют только в одном потоке, то им не требуется синхронизация с этим дескриптором, потому что он автоматически сериализуется.
Очевидно, что вы можете использовать многопакетный подход к (1) и использовать константы времени компиляции для поддержки всех сред, о которых вы знаете.
Вы также можете использовать архитектуру обратного вызова, зависимость от времени соединения или макросы, чтобы ваш вызывающий абонент сообщал вам, как выполнить синхронизацию. Это своего рода смесь (1) и (2).
Но такой вещи, как стандартная многопоточная среда, не существует, поэтому практически невозможно написать автономный код, который является потокобезопасным везде, если он полностью не содержит состояний (то есть все функции не имеют побочных эффектов) , Даже в этом случае вы должны свободно интерпретировать «побочный эффект», поскольку, разумеется, стандарт C не определяет, какие библиотечные функции являются поточно-ориентированными. Это немного похоже на вопрос, как написать код на C, который может выполняться в обработчике аппаратных прерываний. «Что такое прерывание?», Вы можете очень хорошо спросить: «а что я могу сделать в C, недопустимо в одном?». Единственные ответы зависят от ОС.