Могут не быть безопасными в многопоточной среде | Должны быть безопасными в многопоточной среде | Комментарии |
---|---|---|
Asctime | asctime_r | Безопасна в многопоточной среде только в случае непустого аргумента |
ctermid | ||
Ctime | ctime_r | |
getc_unlocked | ||
getchar_unlocked | ||
Getgrid | getgrid_r | |
Getgrnam | getgrnam_r | |
Getlogin | getlogin_r | |
Getpwnam | getpwnam_r | |
Getpwuid | getpwuid_r | |
Gmtime | gmtime_r | |
Localtime | localtime_r | |
putc_unlocked | ||
putchar_unlocked | ||
Rand | rand_r | |
Readdir | readdir_r | |
Strtock | strtock_r | |
tmpnam | Безопасна в многопоточной среде только в случае непустого аргумента | |
Ttyname | ttyname_r | |
GethostXXX | ||
GetnetXXX | ||
GetprotoXXX | ||
GetservXXX | ||
inet_ntoa |
Приведенная таблица позволяет заключить, что общим способом сделать функцию допускающей повторное вхождение является определение новой функции с названием, оканчивающимся на _r
. Обе функции будут безопасными в многопоточной среде, только если вызывающий процесс выделяет в памяти место для результата и передает соответствующий указатель как аргумент функции.
26.5. Собственные данные потоков
При преобразовании существующих функций для использования в многопоточной среде часто возникают проблемы, связанные со статическими переменными. Функция, сохраняющая состояние в собственном буфере или возвращающая результат в виде указателя на статический буфер, не является безопасной в многопоточной среде, поскольку несколько потоков не могут использовать один и тот же буфер для хранения разных данных. Такая проблема имеет несколько решений.
1. Использование собственных данных потоков (thread-specific data). Это нетривиальная задача, и функция при этом преобразуется к такому виду, что может использоваться только в системах, поддерживающих потоки. Преимущество этого подхода заключается в том, что не меняется вызывающая последовательность, и все изменения связаны с библиотечной функцией, а не с приложениями, которые вызывают эту функцию. Позже в этом разделе мы покажем безопасную в многопоточной среде версию функции readline
, созданную с применением собственных данных потоков.
2. Изменение вызывающей последовательности таким образом, чтобы вызывающий процесс упаковывал все аргументы в некую структуру, а также записывал в нее статические переменные из листинга 3.12. Это также было сделано, и в листинге 26.4 показана новая структура и новые прототипы функций.
Листинг 26.4. Структура данных и прототип функции для версии функции readline, допускающей повторное вхождение
typedef struct {
int read_fd; /* дескриптор, указывающий, откуда считываются данные */
char *read_ptr; /* буфер, куда передаются данные */
size_t read_maxlen; /* максимальное количество байтов, которое может быть считано */
/* следующие три элемента для внутреннего использования функцией */
int rl_cnt; /* инициализируется нулем */
char *rl_bufptr; /* инициализируется значением rl_buf */
char rl_buf[MAXLINE];
} Rline;
void readline_rinit(int, void*, size_t, Rline*);
ssize_t readline_r(Rline*);
ssize_t Readline_r(Rline*);
Эти новые функции могут использоваться как в системах с поддержкой потоков, так и в тех, где потоки не поддерживаются, но все приложения, вызывающие функцию readline
, должны быть изменены.
3. Реструктуризация интерфейса для исключения статических переменных и обеспечения безопасности функции в многопоточной среде. Для readline
это будет означать отказ от увеличения быстродействия, достигнутого в листинге 3.12, и возвращение к более старой версии, представленной в листинге 3.11. Поскольку мы назвали старую версию «ужасно медленной», это решение не всегда пригодно на практике.
Использование собственных данных потоков — это распространенный способ сделать существующую функцию безопасной в многопоточной среде. Прежде чем описывать функции Pthread, работающие с такими данными, мы опишем саму концепцию и возможный способ реализации, так как эти функции кажутся более сложными, чем являются на самом деле.