Почему созданные мной темы не распечатаны по порядку? - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть эта программа:

void *func(void *arg) {
    pthread_mutex_lock(&mutex);
    int *id = (int *)arg;

    printf("My ID is %d\n" , *id);
    pthread_mutex_unlock(&mutex);
}

int main() {
    int i;
    pthread_t tid[3];

    // Let us create three threads
    for (i = 0; i < 3; i++) {
        pthread_create(&tid[i], NULL, func, (void *)&i);
    }

    for (i = 0; i < 3; i++) {
        pthread_join(tid[i], NULL);
    }

    pthread_exit(NULL);
    return 0;
}

Я ожидал, что она выведет это:

My ID is 0
My ID is 1
My ID is 2

Но вместо этого я получаю случайный вывод, такой как:

My ID is 0
My ID is 0
My ID is 2

Поскольку я уже добавил блокировку мьютекса, я подумал, что это решит проблему.Что еще я сделал не так?Это связано с расой?

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Здесь id указывает на одну и ту же переменную i в main для всех потоков.

int *id = (int *)arg;

printf("My ID is %d\n" , *id);

Но переменная i постоянно обновляется двумя for -петлями вmain позади темы назад.Поэтому, прежде чем поток достигнет точки printf, значение i, а следовательно, и значение *id, возможно, изменилось.

Есть несколько способов решить эту проблему.Лучший способ зависит от варианта использования:

  1. Подождите main, пока поток не сообщит, что он сделал копию *id, прежде чем изменять i или выпустить его из области видимости.
  2. Объявите и инициализируйте массив, int thread_id[], и создайте потоки следующим образом: pthread_create(&tid[i], NULL, func, &thread_id[i]);
  3. malloc немного памяти и инициализируйте ее с копией i:

    int *thread_id = malloc(sizeof(*thread_id));
    *thread_id = i
    pthread_create(&tid[i], NULL, func, thread_id);
    

    Только не забудьте free вашей памяти в потоке, когда вы закончите его использовать.Или в main, если поток не запускается.

  4. Если i помещается в void *, он может передать его содержимое непосредственно в качестве параметра потоку.Чтобы убедиться, что он подходит, вы можете объявить его как intptr_t вместо int (Мы в основном злоупотребляем тем фактом, что указатели являются не более чем magic целыми числами):

    void *func(void *arg) {
        pthread_mutex_lock(&mutex);
        // Here we interpret a pointer value as an integer value
        intptr_t id = (intptr_t )arg;
    
        printf("My ID is %d\n" , (int)id);
        pthread_mutex_unlock(&mutex);
    }
    
    int main() {
        intptr_t i;
        pthread_t tid[3];
    
        // Let us create three threads
        for (i = 0; i < 3; i++) {
            // Here we squeeze the integer value of `i` into something that is
            // supposed to hold a pointer
            pthread_create(&tid[i], NULL, func, (void *)i);
        }
    
        for (i = 0; i < 3; i++) {
            pthread_join(tid[i], NULL);
        }
    
        // This does not belong here !!
        // pthread_exit(NULL);
        return 0;
    }
    
0 голосов
/ 27 ноября 2018

Нет, условия гонки не задействованы. (my b) Может быть условие гонки на i, потому что все потоки обращаются к нему.Каждый поток начинается с указателя на i.Однако основная проблема заключается в том, что нет гарантии, что поток запустится и запустит критическую секцию, пока я сохраняю ожидаемое вами значение в том порядке, в котором вы ожидаете.

Я предполагаю, что вы объявили переменнуюmutex глобально и вызывается pthread_mutex_init() где-то для его инициализации.

Мьютексы прекрасно подходят для того, чтобы только один поток мог одновременно обращаться к критической секции кода.Таким образом, код, который вы написали, создает все три потока для параллельной работы, но позволяет только одному потоку за раз запускать следующий код.

int *id = (int *)arg;

printf("My ID is %d\n" , *id);
...