Подтвердить что ты не робот

С++: статические переменные в многопоточной программе

В чем проблема со статическими переменными (особенно внутри функций) в многопоточных программах?

Спасибо.

4b9b3361

Ответ 1

Чтобы выбрать иллюстративный пример случайным образом, возьмите интерфейс, например asctime в библиотеке C. Прототип выглядит так:

 char *
 asctime(const struct tm *timeptr);

Это неявно должно иметь некоторый глобальный буфер для хранения символов в возвращаемом char*. Наиболее распространенным и простым способом для этого было бы что-то вроде:

 char *
 asctime(const struct tm *timeptr)
 {
    static char buf[MAX_SIZE];

    /* TODO: convert timeptr into string */

    return buf;
 }

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

Существуют некоторые языковые расширения, которые работают вокруг этой конкретной проблемы в этом конкретном примере через локальное хранилище потоков (__thread, __declspec(thread)). Я полагаю, что эта идея превратила его в С++ 0x как ключевое слово thread_local.

Тем не менее, я бы сказал, что это плохое дизайнерское решение, чтобы использовать его таким образом, по тем же причинам, почему плохо использовать глобальные переменные. Среди прочего, это можно рассматривать как более чистый интерфейс для вызывающего абонента для поддержания и предоставления такого состояния, а не вызываемого. Однако это субъективные аргументы.

Ответ 2

Инициализация не является потокобезопасной. В эту функцию могут войти два потока, и оба могут инициализировать статическую переменную функции-области. Это нехорошо. Там не сказано, какой результат может быть.

В С++ 0x инициализация статических переменных функции-области будет потокобезопасной; первый поток для вызова функции инициализирует переменную и любые другие потоки, которые вызовут эту функцию, чтобы блокировать до тех пор, пока эта инициализация не будет завершена.

Я не думаю, что в настоящее время существуют пары компиляторов + стандартные библиотеки, которые полностью реализуют модель памяти С++ 0x concurrency, а также библиотеки поддержки потоков и атоматики.

Ответ 3

Статическая переменная обычно означает, что множественные вызовы вашей функции будут разделять состояние и, таким образом, мешать друг другу.

Обычно вы хотите, чтобы ваши функции были автономными; имеют локальные копии всего, над чем они работают, и не имеют ничего общего с параметрами внешнего мира и возвращаемыми значениями. (Который, если вы думаете определенным образом, не является частью функции в любом случае.)

Рассмотрим:

int add(int x, int y);

определенно потокобезопасный, локальные копии x и y.

void print(const char *text, Printer *printer);

опасно, кто-то снаружи может что-то делать с одним и тем же принтером, например. вызывая на нем еще один print().

void print(const char *text);

определенно не потокобезопасный, два параллельных вызова гарантированно используют один и тот же принтер.

Конечно, есть способы защитить доступ к общим ресурсам (ключевое слово поиска: mutex); это как раз то, как должно быть ваше чувство кишки.

Несинхронизированная параллельная запись в переменную большую часть времени также не является потоковой, как и чтение и запись. (ключевые слова для поиска: synchronization, synchronization primitives [из которых mutex - только один], также atomicity/atomic operation, если безопасен параллельный доступ.)