Рассмотрим следующий пример. Цель состоит в том, чтобы использовать два потока, один для "вычисления" значения и один, который потребляет и использует вычисленное значение (я попытался упростить это). Вычислительная нить сигнализирует другому потоку, что значение было вычислено и готово с использованием переменной условия, после чего поток ожидания потребляет значение.
// Hopefully this is free from errors, if not, please point them out so I can fix
// them and we can focus on the main question
#include <pthread.h>
#include <stdio.h>
// The data passed to each thread. These could just be global variables.
typedef struct ThreadData
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int spaceHit;
} ThreadData;
// The "computing" thread... just asks you to press space and checks if you did or not
void* getValue(void* td)
{
ThreadData* data = td;
pthread_mutex_lock(&data->mutex);
printf("Please hit space and press enter\n");
data->spaceHit = getchar() == ' ';
pthread_cond_signal(&data->cond);
pthread_mutex_unlock(&data->mutex);
return NULL;
}
// The "consuming" thread... waits for the value to be set and then uses it
void* watchValue(void* td)
{
ThreadData* data = td;
pthread_mutex_lock(&data->mutex);
if (!data->spaceHit)
pthread_cond_wait(&data->cond, &data->mutex);
pthread_mutex_unlock(&data->mutex);
if (data->spaceHit)
printf("You hit space!\n");
else
printf("You did NOT hit space!\n");
return NULL;
}
int main()
{
// Boring main function. Just initializes things and starts the two threads.
pthread_t threads[2];
pthread_attr_t attr;
ThreadData data;
data.spaceHit = 0;
pthread_mutex_init(&data.mutex, NULL);
pthread_cond_init(&data.cond, NULL);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watchValue, &data);
pthread_create(&threads[1], &attr, getValue, &data);
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&data.mutex);
pthread_cond_destroy(&data.cond);
return 0;
}
Мой главный вопрос связан с потенциальной оптимизацией, выполняемой компилятором. Является ли компилятор разрешенными непростая оптимизация и "оптимизация" потока программы, так что происходит следующее:
void* watchValue(void* td)
{
ThreadData* data = td;
pthread_mutex_lock(&data->mutex);
if (!data->spaceHit) // Here, it might remember the result of data->spaceHit
pthread_cond_wait(&data->cond, &data->mutex);
pthread_mutex_unlock(&data->mutex);
if (remember the old result of data->spaceHit without re-getting it)
printf("You hit space!\n");
else
printf("You did NOT hit space!\n");
// The above if statement now may not execute correctly because it didn't
// re-get the value of data->spaceHit, but "remembered" the old result
// from the if statement a few lines above
return NULL;
}
Я немного параноик, что статический анализ компилятора может определить, что data->spaceHit
не изменяется между двумя операторами if
и поэтому оправдывает использование старого значения data->spaceHit
вместо повторного получения нового значения, Я не знаю достаточно о оптимизации потоков и компилятора, чтобы узнать, безопасен ли этот код или нет. Это?
Примечание. Я написал это на C и пометил это как C и С++. Я использую это в библиотеке С++, но поскольку я использую API-интерфейсы потоковой передачи (pthreads и Win32) и имею возможность вставлять C в эту часть библиотеки С++, я отметил это как C и С++.