@Timrus161

Почему это так работает?

import threading;
import random;
import collections;


NUM_THREADS = 5;
COUNT_FOR_EACH_THREAD = 10;
threads = [];
deque = collections.deque();


def threadProc():
	for i in range(0, COUNT_FOR_EACH_THREAD):
		r = random.randint(0, 1000);
		#global deque;
		if r not in deque:
			deque.append(r);

for i in range(0, NUM_THREADS):
	thread = threading.Thread(target = threadProc);
	threads.append(thread);
	thread.start();
	
for i in range(0, NUM_THREADS):
	threads[i].join();

print(deque);


Был написан код с синхронизацией через global. В теории хотел, чтобы было потокобезопасное добавление уникальных рандомных элементов в коллекцию из множества потоков. Все заработало как хотел. потом начал ломать (для теста), закомментировал #global , но элементы все равно начали добавляться в уникальном виде (то есть как будто это не многопоточное приложение, а все происходит в одном потоке). Почему так происходит?
  • Вопрос задан
  • 143 просмотра
Пригласить эксперта
Ответы на вопрос 1
@OlegPyatakov
pyatakov.com
Во-первых, global deque - это не про многопоточность, а про видимость переменных.

Во-вторых, ошибка на самом деле есть в этой части кода:
if r not in deque:
      deque.append(r);

Потенциальная ошибка может появиться во в этом месте, если будет гонка между потоками. Если добавить в этом месте минимальный time.sleep (или другую "затратную" по времени операцию), то все сразу начинает ломаться. Сходу не готов сказать, почему без time.sleep в немодифицированном случае не происходит ошибки (вероятно связано с работой рантайма и GIL). Тем не менее то, что поток не уйдет в сон в этом месте, не гарантируется рантаймом и закладываться на это нельзя.

Чтобы увидеть ошибку, немного я немного модифицировал скрипт: добавил sleep и увеличил количество потоков). У меня в таком виде получается, что добавляется 170 чисел против 101 уникальных.
import threading;
import random;
import collections;

from time import sleep

NUM_THREADS = 50;
COUNT_FOR_EACH_THREAD = 10;
threads = [];
deque = collections.deque();


def threadProc():
  for i in range(0, COUNT_FOR_EACH_THREAD):
    r = random.randint(0, 100);
    if r not in deque:
      sleep(0.01)
      deque.append(r);


for i in range(0, NUM_THREADS):
  thread = threading.Thread(target = threadProc);
  threads.append(thread);
  thread.start();
  
for i in range(0, NUM_THREADS):
  threads[i].join();

print(deque);
print(len(deque))
print(len(set(deque)))


PS. В Python нет смысла оканчивать строки ";".
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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