Как только итератор исчерпан, он больше ничего не даст.
>>> it = iter([3, 1, 2])
>>> for x in it: print(x)
...
3
1
2
>>> for x in it: print(x)
...
>>>
Я нашел две проблемы.
Когда я пытался это сделать в 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;
}
У вас есть несколько проблем в вашем коде, с различной степенью релевантности конкретной проблеме, о которой вы спрашивали. Прежде чем мы пойдем дальше, один вывод , который я получу от запуска вашего кода, как написано, может быть иллюстративным:
blockquote>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
довольно плотный, и поток, который только что разблокировал мьютекс, немедленно попытается снова его заблокировать. Такая попытка повторной блокировки весьма распространена с первой попытки, поскольку поведение мьютекса по умолчанию не дает никаких обещаний относительно справедливости планирования потоков.Кроме того, вы не получаете особой выгоды от своеобразной петли занятости вокруг
1145 В целом, у вас есть проблема со стратегией. Мьютексы, как правило, не дают никаких гарантий относительно справедливого планирования. Обычно для тесного многопоточного взаимодействия, такого как это, вам нужно выполнить какое-то управление расписанием вручную, и это, как правило, значительно выигрывает от добавления условной переменной в смесь.pthread_mutex_trylock()
. Просто используйтеpthread_mutex_lock()
, если в противном случае все, что вы собираетесь сделать, когда мьютекс занят, - это повторить попытку. Это сэкономит ваше процессорное время и не будет существенно отличаться от семантики.
Похоже, вы не хотите заставлять потоки по очереди, но, возможно, этого будет достаточно, чтобы только что запущенный поток не сразу был выбран снова. В этом случае вы можете добавить общую переменную, которая записывает индекс только что запущенного потока, и использовать его, чтобы предотвратить повторный выбор в качестве следующего потока. Схематически функция потока может выглядеть примерно так: цикл
- бесконечно:
- блокировать цикл мьютекса
- бесконечно:
- сравнивать последние индекс потока к моему индексу li>
- если он другой, перерыв в (внутреннем) цикле li>
- еще ожидание переменной условия li> ul>
[1158 ] установить мой индекс как индекс последнего потока- (по-прежнему удерживая мьютекс заблокированным), транслировать или сигнализировать CV
- , если
sum
меньше 100
- update [ 1118] li>
- разблокировать мьютекс li> ul>
- в противном случае (
sum
is> = 100)
- разблокировать мьютекс li> < li> разрыв из (внешнего) цикла li> ul>