Спередать локальные переменные потоку не работает - PullRequest
1 голос
/ 27 марта 2019
#include <stdio.h>
#include <pthread.h>

typedef struct {
    int threadNum;
}thread_args;

void thread_func(void*vargp){
    thread_args*id=(thread_args*)vargp;
    printf("%i\n",id->threadNum);
}

int main() {
    for(int i=0;i<20;i++) {
        pthread_t id;
        thread_args args;
        args.threadNum=i;
        pthread_create(&id,NULL,thread_func,(void*)&args);
    }
    pthread_exit(NULL);
    return 0;
}

Адаптировано с https://www.geeksforgeeks.org/multithreading-c-2/.

Таким образом, ожидается, что это выведет:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Но перетасовано в случайном порядке для учета параллелизма потоков.

Проблема здесь в том, что на самом деле распечатывает это:

4
9
10
5
11
12
13
8
4
4
17
6
18
7
15
19
6
14
19
16

Как видите, есть повторяющиеся числа, а 0-3 просто пропущены.

Ранее я уже выполнял параллелизм в других платформах, и я видел похожие проблемы: здесь происходит то, что i передается как ссылка (я думаю!) И так, когда цикл for увеличивается i, он увеличивается во всех переменных аргумента потока.

Как мне этого избежать?

ПРИМЕЧАНИЕ: все связывается на 100% правильно, и я нахожусь на macOS.

PS: Извините, если это дубликат, я не очень опытен с этим.

Ответы [ 2 ]

4 голосов
/ 27 марта 2019

У вас есть UB для вашего цикла. Вы создаете переменную с именем args, в которой вы присваиваете ей значение, передаете в качестве ссылки на ваш поток для последующего выполнения и уничтожаете его в конце цикла for. Затем вы делаете это снова, возможно перезаписывая этот регион.

Чтобы решить эту проблему, я предлагаю следующую модификацию:

int main() {
    thread_args args[20] = {0};
    pthread_t id[20] = {0};

    for(int i=0;i<20;i++) {
        args[i].threadNum=i;
        pthread_create(&id[i],NULL,thread_func,(void*)&args[i]);
    }

    for(int i = 0; i < 20; i++)
        pthread_join(id[i], NULL);
    return 0;
}
1 голос
/ 27 марта 2019

Это, по сути, состояние гонки.Вы передаете void указатель в структуру аргумента, но (вероятно) один и тот же адрес памяти используется повторно для каждой структуры аргумента.Поэтому при последующем доступе к нему вы, вероятно, будете читать измененную память.Попробуйте это:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct {
    int threadNum;
}thread_args;

void thread_func(void* vargp){
    thread_args* id = (thread_args*)vargp;
    printf("%i\n", id->threadNum);

    free(vargp);
}

int main() {
    for(int i=0;i<20;i++) {
        pthread_t id;
        thread_args* args = malloc(sizeof(thread_args));
        args->threadNum = i;
        pthread_create(&id, NULL, thread_func, (void*)args);
    }
    pthread_exit(NULL);
    return 0;
}

Спасибо Камилу Цуку за указание на другое состояние гонки.

Обратите внимание, что этот фрагмент все еще может просачиваться, потому что код никогда не присоединяется к потокам,так что free() никогда нельзя назвать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...