ПроблемаАвтоматический или локальная переменная потока, сбегающая из потока POSIX®, происходит, когда автоматическая локальная переменная или локальная переменная потока передаются адресом от одного потока POSIX до другого, не гарантируя, что переменная остается в живых через длительность последнего потока.
РискАвтоматическая локальная переменная или локальная переменная потока выделяются на стеке в начале потока, и его время жизни расширяет до конца потока. Переменная, как гарантируют, не будет жива, когда различный поток получит доступ к ней.
Например, рассмотрите функцию запуска потока POSIX с этими линиями:
int start_thread(pthread_t *tid) {
int aVar = 0;
if(thrd_success != pthread_create(tid, NULL, start_thread_child, &aVar) {
//...
}
}
pthread_create
функция создает дочерний поток с функцией запуска start_thread_child
и передает адрес автоматической переменной aVar
к этой функции. Когда этот дочерний поток доступы aVar
, родительский поток может завершить выполнение и aVar
больше не находится на стеке. Доступ может привести к чтению непредсказуемых значений.
ФиксацияКогда вы передаете переменную от одного потока до другого, убедитесь, что переменное время жизни совпадает или превышает время жизни обоих потоков. Можно достигнуть этой синхронизации одним из этих способов:
Объявите переменную static
так, чтобы это не выходило из стека, когда текущий поток завершает выполнение.
Динамически выделите устройство хранения данных для переменной так, чтобы это было выделено на куче вместо стека и должно было быть явным образом освобождено. Убедитесь, что освобождение происходит после обоих потоков полное выполнение.
Эти решения требуют, чтобы вы создали переменную в нелокальной памяти. Вместо этого можно использовать другие решения, такие как shared
ключевое слово с интерфейсом поточной обработки OpenMP, который позволяет вам безопасно совместно использовать локальные переменные через потоки.
Пример – поток выхода локальной переменной#include <pthread.h>
#include <stdio.h>
void* create_child_thread(void *childVal) {
int *res = (int *)childVal;
printf("Result: %d\n", *res);
return NULL;
}
void create_parent_thread(pthread_t *tid) {
int parentVal = 1;
int thrd_success;
if ((thrd_success = pthread_create(tid, NULL, create_child_thread, &parentVal)) != 0) {
/* Handle error */
}
}
int main(void) {
pthread_t tid;
int thrd_success;
create_parent_thread(&tid);
if ((thrd_success = thrd_join(tid, NULL)) != 0) {
/* Handle error */
}
return 0;
}
В этом примере, значение parentVal
локально для родительского потока, который запускается в main
и продолжается в функциональный create_parent_thread
. Однако в теле create_parent_thread
, адрес этой локальной переменной передается дочернему потоку (поток со стандартной программой запуска create_child_thread
). Родительский поток может завершить выполнение и переменную parentVal
может выйти из осциллографа, когда дочерний поток получает доступ к этой переменной.
Коррекция – использует статические переменныеОдна возможная коррекция должна объявить переменную parentVal
как static
так, чтобы переменная была на стеке на целое время программы.
#include <pthread.h>
#include <stdio.h>
void* create_child_thread(void *childVal) {
int *res = (int *)childVal;
printf("Result: %d\n", *res);
return NULL;
}
void create_parent_thread(pthread_t *tid) {
static int parentVal = 1;
int thrd_success;
if ((thrd_success = pthread_create(tid, NULL, create_child_thread, &parentVal)) != 0) {
/* Handle error */
}
}
int main(void) {
pthread_t tid;
int thrd_success;
create_parent_thread(&tid);
if ((thrd_success = thrd_join(tid, NULL)) != 0) {
/* Handle error */
}
return 0;
}
Коррекция – использует динамическое выделение памятиОдна возможная коррекция должна динамически выделить устройство хранения данных для переменных, которые будут совместно использованы через потоки и явным образом освободить устройство хранения данных после того, как переменная больше не будет требоваться.
#include <pthread.h>
#include <stlib.h>
void* create_child_thread(void *val) {
int *res = (int *)val;
printf("Result: %d\n", *res);
free(res);
return NULL;
}
void create_parent_thread(pthread_t *tid) {
int *val;
int thrd_success;
val = malloc(sizeof(int));
if(!val) {
*val = 1;
if ((thrd_success = pthread_create(tid, NULL, create_child_thread, val)) != 0) {
/* Handle error */
}
}
}
int main(void) {
pthread_t tid;
int thrd_success;
create_parent_thread(&tid);
if ((thrd_success = thrd_join(tid, NULL)) != 0) {
/* Handle error */
}
return 0;
}