2016-12-12 86 views
2

我試圖想出一種方法讓線程在同一個目標上工作而不會產生干擾。在這種情況下,我使用4個線程將0到90,000之間的每個數字相加。此代碼運行,但幾乎立即結束(運行時間: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

你有一個錯誤,使得這個程序幾乎立即結束。在Funcwhile z in range(0,i):z沒有在函數中定義,它只有運氣(真的不幸),你碰巧有一個全局變量z = threading.Thread(target=Func, args=(67500,89999,tot4))掩蓋了問題。您正在測試線程對象是否在整數列表中,而不是!

下一個問題是全局變量。首先,你絕對正確的做法是使用單個全局變量不是線程安全的。線程會混淆彼此的計算。但是你誤解了全局變量的工作原理。當您執行threading.Thread(target=Func, args=(67500,89999,tot4))時,python將當前引用的對象tot4傳遞給該函數,但該函數不知道它來自哪個全局。您只更新本地變量tot,並在函數完成時將其丟棄。

解決方案是使用全局容器來保存計算,如下例所示。不幸的是,這實際上比在一個線程中完成所有工作要慢。 python全局解釋器鎖(GIL)只允許1個線程一次運行,並且只會減慢純python中執行的CPU密集型任務。

您可以查看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或只是一個listappend()的結果,例如:

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爲了工作分割爲可以並行運行多個不同的過程。如果在進程之間沒有大量數據需要傳遞,那麼這對CPU密集型任務尤其有益。

下面是一個簡單的實施使用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 
相關問題