2016-12-12 2 views
2

Я пытаюсь придумать способ, чтобы потоки работали над одной и той же целью, не мешая. В этом случае я использую 4 потока, чтобы добавить каждое число от 0 до 90 000. Этот код работает, но он заканчивается почти сразу (Runtime: 0.00399994850159 сек) и только выходы 0. Изначально я хотел сделать это с глобальной переменной, но меня беспокоило потоки, мешающие друг другу (т. Е. Небольшая вероятность того, что два потока удваиваются подсчитать или пропустить номер из-за странного времени чтения/записи). Поэтому вместо этого я заранее распределял рабочую нагрузку. Если есть лучший способ сделать это, пожалуйста, поделитесь. Это мой простой способ получить опыт в многопоточности. СпасибоМогут ли потоки Python работать над одним и тем же процессом?

import threading 
import time 

start_time = time.time() 

tot1 = 0 
tot2 = 0 
tot3 = 0 
tot4 = 0 

def Func(x,y,tot): 
    tot = 0 
    i = y-x 
    while z in range(0,i): 
     tot = tot + i + z 

# class Tester(threading.Thread): 
# def run(self): 
#  print(n) 

w = threading.Thread(target=Func, args=(0,22499,tot1)) 
x = threading.Thread(target=Func, args=(22500,44999,tot2)) 
y = threading.Thread(target=Func, args=(45000,67499,tot3)) 
z = threading.Thread(target=Func, args=(67500,89999,tot4)) 

w.start() 
x.start() 
y.start() 
z.start() 

w.join() 
x.join() 
y.join() 
z.join() 

# while (w.isAlive() == False | x.isAlive() == False | y.isAlive() == False | z.isAlive() == False): {} 

total = tot1 + tot2 + tot3 + tot4 

print total 

print("--- %s seconds ---" % (time.time() - start_time)) 

ответ

0

У вас есть ошибка, которая заставляет эту программу заканчиваться почти сразу. Посмотрите на while z in range(0,i): в Func. z не определен в функции и ее единственная удача (неудача на самом деле), что у вас есть глобальная переменная z = threading.Thread(target=Func, args=(67500,89999,tot4)), которая маскирует проблему. Вы проверяете, находится ли объект потока в списке целых чисел ... и его нет!

Следующая проблема связана с глобальными переменными. Во-первых, вы абсолютно правы, что использование одной глобальной переменной не является потокобезопасной. Нити будут возиться с вычислениями друг друга. Но вы неправильно понимаете, как работают глобальные переменные. Когда вы делаете threading.Thread(target=Func, args=(67500,89999,tot4)), python передает объект, ссылающийся на объект в настоящее время на tot4, но функция не знает, из какого глобального он пришел. Вы обновляете локальную переменную tot и отбрасываете ее, когда функция завершается.

Решение состоит в использовании глобального контейнера для проведения расчетов, как показано в примере ниже. К сожалению, это на самом деле медленнее, чем просто выполнение всей работы в одном потоке. Блокировка глобального интерпретатора python (GIL) позволяет только одному потоку запускать за один раз и только замедляет задачи с интенсивным использованием процессора, реализованные в чистом питоне.

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

Вот рабочая копия вашего примера:

import threading 
import time 

start_time = time.time() 

tot = [0] * 4 

def Func(x,y,tot_index): 
    my_total = 0 
    i = y-x 
    for z in range(0,i): 
     my_total = my_total + i + z 
    tot[tot_index] = my_total 

# class Tester(threading.Thread): 
# def run(self): 
#  print(n) 

w = threading.Thread(target=Func, args=(0,22499,0)) 
x = threading.Thread(target=Func, args=(22500,44999,1)) 
y = threading.Thread(target=Func, args=(45000,67499,2)) 
z = threading.Thread(target=Func, args=(67500,89999,3)) 

w.start() 
x.start() 
y.start() 
z.start() 

w.join() 
x.join() 
y.join() 
z.join() 

# while (w.isAlive() == False | x.isAlive() == False | y.isAlive() == False | z.isAlive() == False): {} 

total = sum(tot) 


print total 

print("--- %s seconds ---" % (time.time() - start_time)) 
0

Вы можете передать в изменяемом объекте, который вы можете добавить свои результаты либо с идентификатором, например, dict или просто list и append() результаты, например .:

import threading 

def Func(start, stop, results): 
    results.append(sum(range(start, stop+1))) 

rngs = [(0, 22499), (22500, 44999), (45000, 67499), (67500, 89999)] 
results = [] 
jobs = [threading.Thread(target=Func, args=(start, stop, results)) for start, stop in rngs] 

for j in jobs: 
    j.start() 

for j in jobs: 
    j.join() 

print(sum(results)) 
# 4049955000 
# 100 loops, best of 3: 2.35 ms per loop 
0

Как и другие отметили, вы можете посмотреть multiprocessing для того, чтобы разделить работу на несколько различных процессов, которые могут выполняться параллельно. Это было бы особенно полезно для задач с интенсивным использованием ЦП, предполагая, что между процессами не будет огромного количества данных.

Вот простая реализация той же функциональности с использованием multiprocessing:

from multiprocessing import Pool 

POOL_SIZE = 4 
NUMBERS = 90000 

def func(_range): 
    tot = 0 
    for z in range(*_range): 
     tot += z 

    return tot 

with Pool(POOL_SIZE) as pool: 
    chunk_size = int(NUMBERS/POOL_SIZE) 
    chunks = ((i, i + chunk_size) for i in range(0, NUMBERS, chunk_size)) 
    print(sum(pool.imap(func, chunks))) 

В выше chunks представляет собой генератор, который производит те же самые диапазоны, которые были жестко прописаны в оригинальной версии. Он дается imap, который работает так же, как и стандартный map, за исключением того, что он выполняет функцию в процессах в пуле.

Менее известный факт о multiprocessing состоит в том, что вы можете легко преобразовать код, чтобы использовать потоки вместо процессов, используя недокументированные multiprocessing.pool.ThreadPool. Чтобы преобразовать приведенный выше пример для использования потоков, просто измените import на:

from multiprocessing.pool import ThreadPool as Pool 

 Смежные вопросы

  • Нет связанных вопросов^_^