Как я могу выполнить несколько потоков, чтобы объединить несколько раз в процесс? (Pthread_mutex_lock)

Как только итератор исчерпан, он больше ничего не даст.

>>> it = iter([3, 1, 2])
>>> for x in it: print(x)
...
3
1
2
>>> for x in it: print(x)
...
>>>
-1
задан aqamn 16 January 2019 в 15:06
поделиться

2 ответа

Я нашел две проблемы.

Когда я пытался это сделать в Windows с MSys2, мой вывод был

The 1. thread got 101 points!
The 1. thread got 101 points!
The 2. thread got 0 points!
The 3. thread got 0 points!
The 5. thread got 0 points!
The 6. thread got 0 points!
The 6. thread got 0 points!
The 8. thread got 0 points!
The 9. thread got 0 points!
The 0. thread got 0 points!

Вы передаете адрес вашего счетчика циклов i потокам. Эта переменная не является константой, поэтому потоки могут прочитать значение, которое уже изменилось. Чтобы исправить это, я создал массив int ids[10] и передал каждому потоку свой собственный элемент массива идентификаторов.

Функция sleep использует аргумент unsigned int, поэтому вы эффективно вызываете sleep(0). С sleep(1) программа работает медленнее, но работает как положено. Вы можете использовать usleep, если вы хотите спать в течение доли секунды.

Измененная программа:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

pthread_mutex_t mutex;
int arr[10];
int sum = 0;

void *add (void* input){

    int *id = (int*) input, x, s;
    int ind = *id;
    while (sum < 100){
        while (1){
            if (pthread_mutex_trylock(&mutex) != EBUSY)
                break;
        }
        if (sum < 100){
            x = rand() % 3;
            arr[ind] = arr[ind] + x;
            sum += x;
        }
        pthread_mutex_unlock(&mutex);
        usleep(100000);
    }
    printf("The %d. thread got %d points!\n", ind, arr[ind]);
    return NULL;
}

int main (void){

    int i;
    pthread_t threads[10];
    int ids[10];
    pthread_mutex_init(&mutex, NULL);
    srand(time(NULL));
    for (i = 0; i < 10; i++){
        ids[i] = i;
        if (pthread_create(&threads[i], NULL, add, &ids[i])){
            perror("pthread_create");
            exit(1);
        }
    }
    for (i = 0; i < 10; i++){
        if (pthread_join (threads[i], NULL)){
            perror("pthread_join");
        }
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}
0
ответ дан Bodo 16 January 2019 в 15:06
поделиться

У вас есть несколько проблем в вашем коде, с различной степенью релевантности конкретной проблеме, о которой вы спрашивали. Прежде чем мы пойдем дальше, один вывод , который я получу от запуска вашего кода, как написано, может быть иллюстративным:

The 2. thread got 100 points!
The 8. thread got 0 points!
The 9. thread got 0 points!
The 5. thread got 0 points!
The 5. thread got 0 points!
The 6. thread got 0 points!
The 7. thread got 0 points!
The 2. thread got 100 points!
The 4. thread got 0 points!
The 1. thread got 0 points!

Обратите внимание, что некоторые потоки получили один и тот же индекс счетчика. , Это может произойти из-за гонки данных между основным потоком и каждым дочерним потоком: основной поток передает указатель на свою локальную переменную i, а затем переходит к изменению значения этой переменной. Между тем, дочерний поток читает значение переменной через указатель. Эти действия не синхронизированы, поэтому поведение не определено.

Эта конкретная проблема имеет несколько решений. Простейшим, вероятно, является приведение i к void * и передача результата (по значению). На самом деле это довольно часто, когда вы просто хотите передать целое число:

        if (pthread_create(&threads[i], NULL, add, (void *)i)) {
            // ...

Конечно, функция потока должна преобразовать его обратно:

void *add (void* input) {
    int /*no need for 'id' */ x, s;
    int ind = (int) input;
    // ...

Далее, обратите внимание, что вы иметь другую гонку данных среди всех ваших дочерних потоков, где вы читаете значение sum в состоянии цикла while. Тот, кажется, не кусает вас в данный момент, но это может сделать в любое время. Поскольку потоки, как группа, читают и изменяют sum, вы должны обеспечить синхронизацию всех таких обращений, например, выполняя их только при удержании мьютекса заблокированным.

Пропустив немного (мы вернемся), у вас проблема с вашим вызовом sleep(). Параметр этой функции имеет тип int, поэтому ваш фактический аргумент, double 0,1, преобразуется в int, что приводит к 0. Компилятор может оптимизировать это вообще, а если нет, то он может эффективно быть неоператором. Еще более важно , однако. sleep() просто неправильный инструмент для этой работы или почти для любой работы, связанной с синхронизацией между потоками и синхронизацией.

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

Кроме того, вы не получаете особой выгоды от своеобразной петли занятости вокруг pthread_mutex_trylock(). Просто используйте pthread_mutex_lock(), если в противном случае все, что вы собираетесь сделать, когда мьютекс занят, - это повторить попытку. Это сэкономит ваше процессорное время и не будет существенно отличаться от семантики.

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

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

  • бесконечно:
    • блокировать цикл мьютекса
    • бесконечно:
      • сравнивать последние индекс потока к моему индексу
      • если он другой, перерыв в (внутреннем) цикле
      • еще ожидание переменной условия
      • [1158 ] установить мой индекс как индекс последнего потока
      • (по-прежнему удерживая мьютекс заблокированным), транслировать или сигнализировать CV
      • , если sum меньше 100
        • update [ 1118]
        • разблокировать мьютекс
        • в противном случае (sum is> = 100)
          • разблокировать мьютекс < li> разрыв из (внешнего) цикла
0
ответ дан John Bollinger 16 January 2019 в 15:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: