Избегайте условий гонки при использовании указателей и потоков - PullRequest
2 голосов
/ 28 июня 2019

Я для собственного удовольствия возиться с этим фрагментом кода:

#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <stdio.h>

void print_message(void *param)
{
    int thread_counter = *((int*)param);
    printf("Thread #%i\r\n", thread_counter);
    _endthreadex(0);
}

int main()
{
    unsigned thread_ID;
    HANDLE thread_handle;

    int thread_count = 10;

    HANDLE thread_handles[thread_count];

    for(int i = 0; i < thread_count; i++) 
    {
        thread_handles[i] = (HANDLE)_beginthreadex(NULL, 0, &print_message, &i, 0, &thread_ID);
        thread_handle = thread_handles[i];
    }

    WaitForMultipleObjects(thread_count, thread_handles, TRUE, INFINITE);
    return EXIT_SUCCESS;
}

а

Output
Thread #8
Thread #10
Thread #10
Thread #10
Thread #10
Thread #10
Thread #10
Thread #10
Thread #10
Thread #10

Мне кажется, проблема в том, что я передаю ссылку на переменную i, а переменная i увеличивается в цикле for, пока потоки используют / управляют этой ссылкой.

Как мне избежать условия гонки и передать значение переменной i в функцию потока?

Я также хотел бы передать массивы строк / символов в функцию потока, но, похоже, я не понимаю, как это сделать.

Я использую Tiny C Compiler (TCC) в Windows 10

Ответы [ 3 ]

3 голосов
/ 28 июня 2019

Не передавайте указатель на i, передавайте значение , хранящееся в самом i:

void print_message(void *param)
{
    int thread_counter = (intptr_t)param;
    printf("Thread #%i\r\n", thread_counter);
    _endthreadex(0);
}

// ... rest of code ...
    // We cast the value in i to a pointer width integer value, intptr_t
    // to explicitly match the size of the void* we're smuggling it in
    thread_handles[i] = (HANDLE)_beginthreadex(NULL, 0, &print_message, (void*)(intptr_t)i, 0, &thread_ID);
// ... rest of code ...

Если все, что выПередача является единственным, меньшим, чем значение ширины указателя, вы можете переправить его значение как само значение «указателя».В этом случае вы просто повышаете его до intptr_t (целочисленный тип, соответствующий ширине указателя) и понижаете его при извлечении.

Поскольку передается фактическое значение, а не указатель на него,Копирование выполняется до вызова _beginthreadex, а изменение i после этого ничего не меняет.

Если передаваемые данные больше, то вы застряли либо наложив какой-либо барьер (чтобы убедиться, что значение прочитано до того, как основной поток снова коснется i) или с использованием динамической памяти (выделите пространство, скопируйте значения, передайте указатель на выделенную память потоку, поток извлечет значения перед освобождением пространства).Третий параметр - это массив значений, по одному для каждого потока (поэтому первый поток получает &arr[i], где i равен 0, следующий &arr[i] для i == 1 и т. Д.).

2 голосов
/ 28 июня 2019

Вы можете передать значение вместо указателя, похожего на это.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

void print(void *foo){
    intptr_t bar = (intptr_t)foo; 
    printf("%" PRIdPTR "\n",bar); 
}

int main(void)
{
    intptr_t i=1;
    print((void*)i);
    return 0;
}

Вам просто нужно быть осторожным, чтобы функция действительно воспринимала это как значение.В самой функции вы должны убедиться, что переменная, которую вы приводите, может принимать размер указателя.(это делается с помощью intptr_t)

Чтобы вообще избежать условий гонки, вы можете использовать мьютексы, но здесь это не очень полезно.

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

0 голосов
/ 28 июня 2019

Посмотрите на мьютексы: https://www.thegeekstuff.com/2012/05/c-mutex-examples/, если вы хотите разделить переменные между несколькими потоками.

Мьютексы позволяют вам «блокировать» переменную так, чтобы только один поток мог использовать ее одновременно.

...