成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

python綜合學(xué)習(xí)一之多線程

cjie / 818人閱讀

摘要:如下面的例子,在學(xué)習(xí)線程時(shí),將文件名命名為腳本完全正常沒問題,結(jié)果報(bào)下面的錯(cuò)誤。最大的問題就是的多線程程序并不能利用多核的優(yōu)勢(shì)比如一個(gè)使用了多個(gè)線程的計(jì)算密集型程序只會(huì)在一個(gè)單上面運(yùn)行。

本文記錄學(xué)習(xí)Python遇到的問題和一些常用用法,注本開發(fā)環(huán)境的Python版本為2.7。
一、python文件命名

在python文件命名時(shí),一定要注意不能和系統(tǒng)默認(rèn)的模塊名沖突,否則會(huì)報(bào)錯(cuò)。
如下面的例子,在學(xué)習(xí)線程時(shí),將文件名命名為 threading.py,Python腳本完全正常沒問題,結(jié)果報(bào)下面的錯(cuò)誤:AttributeError: "module" object has no attribute "xxx"。

threading.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_test.py
@time: 18/8/25 09:14
"""

import threading

# 獲取已激活的線程數(shù)
print(threading.active_count())

執(zhí)行:

?  baseLearn python threading/threading.py
Traceback (most recent call last):
  File "threading/threading.py", line 9, in 
    import threading
  File "/Users/kaiyiwang/Code/python/baseLearn/threading/threading.py", line 12, in 
    print(threading.active_count())
AttributeError: "module" object has no attribute "active_count"
?  baseLearn
問題定位:

查看import庫(kù)的源文件,發(fā)現(xiàn)源文件存在且沒有錯(cuò)誤,同時(shí)存在源文件的.pyc文件

問題解決:

1.命名py腳本時(shí),不要與python預(yù)留字,模塊名等相同

2.刪除該庫(kù)的.pyc文件(因?yàn)閜y腳本每次運(yùn)行時(shí)均會(huì)生成.pyc文件;在已經(jīng)生成.pyc文件的情況下,若代碼不更新,運(yùn)行時(shí)依舊會(huì)走pyc,所以要?jiǎng)h除.pyc文件),重新運(yùn)行代碼;或者找一個(gè)可以運(yùn)行代碼的環(huán)境,拷貝替換當(dāng)前機(jī)器的.pyc文件即可

將腳本文件名重新命名為threading_test.py,然后執(zhí)行,就不會(huì)報(bào)錯(cuò)了。

?  baseLearn python threading/threading_test.py
1
?  baseLearn
二、多線程threading

多線程是加速程序計(jì)算的有效方式,Python的多線程模塊threading上手快速簡(jiǎn)單,從這節(jié)開始我們就教大家如何使用它。

1、添加線程

threading_test.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_test.py
@time: 18/8/25 09:14
"""

import threading

# 獲取已激活的線程數(shù)
# print(threading.active_count())

# 查看所有線程信息
# print(threading.enumerate())

# 查看現(xiàn)在正在運(yùn)行的線程
# print(threading.current_thread())

def thread_job():
    print("This is a thread of %s" % threading.current_thread())


def main():
    thread = threading.Thread(target=thread_job,)  # 定義線程
    thread.start() # 讓線程開始工作

if __name__ == "__main__":
    main()
2、join功能 不加 join() 的結(jié)果

我們讓 T1 線程工作的耗時(shí)增加

threading_join.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def thread_job():
    print("T1 start
")
    for i in range(10):
        time.sleep(0.1) # 任務(wù)時(shí)間0.1s
    print("T1 finish
")


def main():
    added_thread = threading.Thread(target=thread_job, name="T1")  # 定義線程
    added_thread.start() # 讓線程開始工作
    print("all done
")

if __name__ == "__main__":
    main()

預(yù)想中輸出的結(jié)果是按照順序依次往下執(zhí)行:

T1 start
T1 finish
all done

但實(shí)際運(yùn)行結(jié)果為:

?  baseLearn python threading/threading_join.py
T1 start
all done


T1 finish

?  baseLearn
加入join()的結(jié)果

線程任務(wù)還未完成便輸出all done。如果要遵循順序,可以在啟動(dòng)線程后對(duì)它調(diào)用join

added_thread.start()
added_thread.join()
print("all done
")

打印結(jié)果:

?  baseLearn python threading/threading_join.py
T1 start

T1 finish

all done

完整腳本文件:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def thread_job():
    print("T1 start
")
    for i in range(10):
        time.sleep(0.1) # 任務(wù)時(shí)間0.1s
    print("T1 finish
")


def main():
    added_thread = threading.Thread(target=thread_job, name="T1")  # 定義線程
    added_thread.start() # 讓線程開始工作
    added_thread.join()
    print("all done
")

if __name__ == "__main__":
    main()

小試牛刀

如果添加兩個(gè)線程,打印的輸出結(jié)果是怎樣的呢?

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_join.py
@time: 18/8/25 09:14
"""

import threading
import time

def T1_job():
    print("T1 start
")
    for i in range(10):
        time.sleep(0.1) # 任務(wù)時(shí)間0.1s
    print("T1 finish
")

def T2_job():
    print("T2 start
")
    print("T2 finish
")

def main():
    thread_1 = threading.Thread(target=T1_job, name="T1")  # 定義線程
    thread_2 = threading.Thread(target=T2_job, name="T2")  # 定義線程
    thread_1.start()  # 開啟T1
    thread_2.start()  # 開啟T2
    print("all done
")

if __name__ == "__main__":
    main()

輸出的”一種”結(jié)果是:

T1 start

T2 start

T2 finish

all done

T1 finish

現(xiàn)在T1和T2都沒有join,注意這里說”一種”是因?yàn)閍ll done的出現(xiàn)完全取決于兩個(gè)線程的執(zhí)行速度, 完全有可能T2 finish出現(xiàn)在all done之后。這種雜亂的執(zhí)行方式是我們不能忍受的,因此要使用join加以控制。

我們?cè)囋囋赥1啟動(dòng)后,T2啟動(dòng)前加上thread_1.join():

thread_1.start()
thread_1.join() # notice the difference!
thread_2.start()
print("all done
")

打印結(jié)果:

T1 start

T1 finish

T2 start
all done

T2 finish

可以看到,T2會(huì)等待T1結(jié)束后才開始運(yùn)行。

3、儲(chǔ)存進(jìn)程結(jié)果Queue 實(shí)現(xiàn)功能

代碼實(shí)現(xiàn)功能,將數(shù)據(jù)列表中的數(shù)據(jù)傳入,使用四個(gè)線程處理,將結(jié)果保存在Queue中,線程執(zhí)行完后,從Queue中獲取存儲(chǔ)的結(jié)果

在多線程函數(shù)中定義一個(gè)Queue,用來保存返回值,代替return,定義一個(gè)多線程列表,初始化一個(gè)多維數(shù)據(jù)列表,用來處理:

threading_queue.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_queue.py
@time: 18/8/25 09:14
"""

import threading
import time
from queue import Queue

def job(l, q):
    for i in range(len(l)):
        l[i] = l[i] ** 2
    q.put(l) #多線程調(diào)用的函數(shù)不能用return返回值

def multithreading():
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []
    data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]

    for i in range(4): #定義四個(gè)線程
        t = threading.Thread(target=job, args=(data[i], q))  #Thread首字母要大寫,被調(diào)用的job函數(shù)沒有括號(hào),只是一個(gè)索引,參數(shù)在后面
        t.start() #開始線程
        threads.append(t) #把每個(gè)線程append到線程列表中

    for thread in threads:
        thread.join()

    results = []
    for _ in range(4):
        results.append(q.get()) #q.get()按順序從q中拿出一個(gè)值
    print(results)


if __name__ == "__main__":
    multithreading()

執(zhí)行上邊的腳本出現(xiàn)了這樣的錯(cuò)誤:

?  baseLearn python threading/threading_queue.py
Traceback (most recent call last):
  File "threading/threading_queue.py", line 11, in 
    from queue import Queue
ImportError: No module named queue

查了下原因,是因?yàn)閜ython版本導(dǎo)致的:
解決方法:No module named "Queue"

On Python 2, the module is named Queue, on Python 3, it was renamed to follow PEP8 guidelines (all lowercase for module names), making it queue. The class remains Queue on all versions (following PEP8).

Typically, the way you"d write version portable imports would be to do:

python3 中這樣引用:

try:
    import queue
except ImportError:
    import Queue as queue

在 python2 中 我們可以這樣引用:

from Queue import Queue

打印:

baseLearn python ./threading/threading_queue.py
[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]

完整代碼:
threading_queue.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_queue.py
@time: 18/8/25 09:14
"""

import threading
# import time
from Queue import Queue

def job(l, q):
    for i in range(len(l)):
        l[i] = l[i] ** 2
    q.put(l) #多線程調(diào)用的函數(shù)不能用return返回值

def multithreading():
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []
    data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]

    for i in range(4): #定義四個(gè)線程
        t = threading.Thread(target=job, args=(data[i], q))  #Thread首字母要大寫,被調(diào)用的job函數(shù)沒有括號(hào),只是一個(gè)索引,參數(shù)在后面
        t.start() #開始線程
        threads.append(t) #把每個(gè)線程append到線程列表中

    for thread in threads:
        thread.join()

    results = []
    for _ in range(4):
        results.append(q.get()) #q.get()按順序從q中拿出一個(gè)值
    print(results)


if __name__ == "__main__":
    multithreading()
4、GIL效率問題 何為 GIL?

這次我們來看看為什么說 python 的多線程 threading 有時(shí)候并不是特別理想. 最主要的原因是就是, Python 的設(shè)計(jì)上, 有一個(gè)必要的環(huán)節(jié), 就是 Global Interpreter Lock (GIL)。 這個(gè)東西讓 Python 還是一次性只能處理一個(gè)東西。

GIL的解釋:

盡管Python完全支持多線程編程, 但是解釋器的C語言實(shí)現(xiàn)部分在完全并行執(zhí)行時(shí)并不是線程安全的。 實(shí)際上,解釋器被一個(gè)全局解釋器鎖保護(hù)著,它確保任何時(shí)候都只有一個(gè)Python線程執(zhí)行。 GIL最大的問題就是Python的多線程程序并不能利用多核CPU的優(yōu)勢(shì) (比如一個(gè)使用了多個(gè)線程的計(jì)算密集型程序只會(huì)在一個(gè)單CPU上面運(yùn)行)。
在討論普通的GIL之前,有一點(diǎn)要強(qiáng)調(diào)的是GIL只會(huì)影響到那些嚴(yán)重依賴CPU的程序(比如計(jì)算型的)。 如果你的程序大部分只會(huì)涉及到I/O,比如網(wǎng)絡(luò)交互,那么使用多線程就很合適, 因?yàn)樗鼈兇蟛糠謺r(shí)間都在等待。實(shí)際上,你完全可以放心的創(chuàng)建幾千個(gè)Python線程, 現(xiàn)代操作系統(tǒng)運(yùn)行這么多線程沒有任何壓力,沒啥可擔(dān)心的。
測(cè)試GIL

我們創(chuàng)建一個(gè) job, 分別用 threading 和 一般的方式執(zhí)行這段程序. 并且創(chuàng)建一個(gè) list 來存放我們要處理的數(shù)據(jù). 在 Normal 的時(shí)候, 我們這個(gè) list 擴(kuò)展4倍, 在 threading 的時(shí)候, 我們建立4個(gè)線程, 并對(duì)運(yùn)行時(shí)間進(jìn)行對(duì)比.

threading_gil.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_gil.py
@time: 18/8/25 09:14
"""

import threading
from Queue import Queue
import copy
import time

def job(l, q):
    res = sum(l)
    q.put(l) #多線程調(diào)用的函數(shù)不能用return返回值

def multithreading(l):
    q = Queue()  #q中存放返回值,代替return的返回值
    threads = []

    for i in range(4): #定義四個(gè)線程
        t = threading.Thread(target=job, args=(copy.copy(l), q), name="T%i" % i)  #Thread首字母要大寫,被調(diào)用的job函數(shù)沒有括號(hào),只是一個(gè)索引,參數(shù)在后面
        t.start() #開始線程
        threads.append(t) #把每個(gè)線程append到線程列表中

    [t.join() for t in threads]
    total = 0
    for _ in range(4):
        total = q.get() #q.get()按順序從q中拿出一個(gè)值
    print(total)

def normal(l):
    total = sum(l)
    print(total)

if __name__ == "__main__":
    l = list(range(1000000))
    s_t = time.time()
    normal(l*4)
    print("normal:", time.time() - s_t)
    s_t = time.time()
    multithreading(l)
    print("multithreading: ", time.time() - s_t)

如果你成功運(yùn)行整套程序, 你大概會(huì)有這樣的輸出. 我們的運(yùn)算結(jié)果沒錯(cuò), 所以程序 threading 和 Normal 運(yùn)行了一樣多次的運(yùn)算. 但是我們發(fā)現(xiàn) threading 卻沒有快多少, 按理來說, 我們預(yù)期會(huì)要快3-4倍, 因?yàn)橛薪?個(gè)線程, 但是并沒有. 這就是其中的 GIL 在作怪.

1999998000000
normal:  0.10034608840942383
1999998000000
multithreading:  0.08421492576599121
5、線程鎖Lock 不使用 Lock 的情況

threading_lock.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_lock.py
@time: 18/8/25 09:14
"""

import threading

# 全局變量A的值每次加1,循環(huán)10次,并打印
def job1():
    global A
    for i in range(10):
        A+=1
        print("job1",A)

# 全局變量A的值每次加10,循環(huán)10次,并打印
def job2():
    global A
    for i in range(10):
        A+=10
        print("job2",A)

# 定義兩個(gè)線程,分別執(zhí)行函數(shù)一和函數(shù)二
if __name__== "__main__":
   
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

打印輸出數(shù)據(jù):

?  baseLearn python ./threading/threading_lock.py
("job1", ("job2"1)
, (11)"job1"
("job2", 22)
("job2", 32)
("job2", 42)
("job2", 52)
("job2", 62)
("job2", 72)
("job2", 82)
("job2", 92)
("job2", 102)
, 12)
("job1", 103)
("job1", 104)
("job1", 105)
("job1", 106)
("job1", 107)
("job1", 108)
("job1", 109)
("job1", 110)

可以看出,打印的結(jié)果非?;靵y

使用 Lock 的情況

lock在不同線程使用同一共享內(nèi)存時(shí),能夠確保線程之間互不影響,使用lock的方法是, 在每個(gè)線程執(zhí)行運(yùn)算修改共享內(nèi)存之前,執(zhí)行lock.acquire()將共享內(nèi)存上鎖, 確保當(dāng)前線程執(zhí)行時(shí),內(nèi)存不會(huì)被其他線程訪問,執(zhí)行運(yùn)算完畢后,使用lock.release()將鎖打開, 保證其他的線程可以使用該共享內(nèi)存。

函數(shù)一和函數(shù)二加鎖

def job1():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=1
        print("job1",A)
    lock.release()

def job2():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=10
        print("job2",A)
    lock.release()

主函數(shù)中定義一個(gè)Lock

if __name__== "__main__":
    lock=threading.Lock()
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

完整代碼:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: threading_lock.py
@time: 18/8/25 09:14
"""

import threading

def job1():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=1
        print("job1",A)
    lock.release()

def job2():
    global A,lock
    lock.acquire()
    for i in range(10):
        A+=10
        print("job2",A)
    lock.release()

if __name__== "__main__":
    lock = threading.Lock()
    A=0
    t1=threading.Thread(target=job1)
    t2=threading.Thread(target=job2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

打印輸出:

?  baseLearn python ./threading/threading_lock.py
("job1", 1)
("job1", 2)
("job1", 3)
("job1", 4)
("job1", 5)
("job1", 6)
("job1", 7)
("job1", 8)
("job1", 9)
("job1", 10)
("job2", 20)
("job2", 30)
("job2", 40)
("job2", 50)
("job2", 60)
("job2", 70)
("job2", 80)
("job2", 90)
("job2", 100)
("job2", 110)

從打印結(jié)果來看,使用lock后,一個(gè)一個(gè)線程執(zhí)行完。使用lock和不使用lock,最后打印輸出的結(jié)果是不同的。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/42316.html

相關(guān)文章

  • python綜合學(xué)習(xí)之多進(jìn)程

    摘要:本節(jié)講學(xué)習(xí)的多進(jìn)程。和之前的的不同點(diǎn)是丟向的函數(shù)有返回值,而的沒有返回值。所以接下來讓我們來看下這兩個(gè)進(jìn)程是否會(huì)出現(xiàn)沖突。 本節(jié)講學(xué)習(xí)Python的多進(jìn)程。 一、多進(jìn)程和多線程比較 多進(jìn)程 Multiprocessing 和多線程 threading 類似, 他們都是在 python 中用來并行運(yùn)算的. 不過既然有了 threading, 為什么 Python 還要出一個(gè) multip...

    gityuan 評(píng)論0 收藏0
  • Python爬蟲學(xué)習(xí)路線

    摘要:以下這些項(xiàng)目,你拿來學(xué)習(xí)學(xué)習(xí)練練手。當(dāng)你每個(gè)步驟都能做到很優(yōu)秀的時(shí)候,你應(yīng)該考慮如何組合這四個(gè)步驟,使你的爬蟲達(dá)到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學(xué)習(xí)不是一朝一夕的事情,建議多看看一些比較優(yōu)秀的爬蟲的設(shè)計(jì)方案,比如說。 (一)如何學(xué)習(xí)Python 學(xué)習(xí)Python大致可以分為以下幾個(gè)階段: 1.剛上手的時(shí)候肯定是先過一遍Python最基本的知識(shí),比如說:變量、數(shù)據(jù)結(jié)構(gòu)、語法...

    liaoyg8023 評(píng)論0 收藏0
  • 零基礎(chǔ)如何學(xué)爬蟲技術(shù)

    摘要:楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),。本文來源知乎作者路人甲鏈接楚江數(shù)據(jù)提供網(wǎng)站數(shù)據(jù)采集和爬蟲軟件定制開發(fā)服務(wù),服務(wù)范圍涵蓋社交網(wǎng)絡(luò)電子商務(wù)分類信息學(xué)術(shù)研究等。 楚江數(shù)據(jù)是專業(yè)的互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)服務(wù),現(xiàn)整理出零基礎(chǔ)如何學(xué)爬蟲技術(shù)以供學(xué)習(xí),http://www.chujiangdata.com。 第一:Python爬蟲學(xué)習(xí)系列教程(來源于某博主:htt...

    KunMinX 評(píng)論0 收藏0
  • jvm基礎(chǔ)篇一之內(nèi)存區(qū)域

    摘要:堆區(qū)堆是虛擬機(jī)所管理的內(nèi)存中最大的一塊,它是被所有線程共享的一塊內(nèi)存區(qū)域,該區(qū)域在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建。 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 ? ?想要了解jvm,那對(duì)其內(nèi)存分配管理的學(xué)習(xí)是必不可少的;java虛擬機(jī)在執(zhí)行java程序的時(shí)候會(huì)把它所管理的內(nèi)存劃分成若干數(shù)據(jù)區(qū)域。這些區(qū)域有著不同的功能、用途、創(chuàng)建/銷毀時(shí)間。java虛擬機(jī)所分配管理的內(nèi)存區(qū)域如圖1所示 程序計(jì)數(shù)器 ? ?程序計(jì)數(shù)器是一塊比較...

    Zachary 評(píng)論0 收藏0
  • Python爬蟲之多線程下載豆瓣Top250電影圖片

    摘要:本次爬蟲項(xiàng)目將會(huì)用到模塊中的類,多線程豆瓣電影圖片??偨Y(jié)通過上述兩個(gè)爬蟲程序的對(duì)比,我們不難發(fā)現(xiàn),同樣是下載豆瓣電影,個(gè)網(wǎng)頁(yè)中的圖片,在沒有使用多線程的情況下,總共耗時(shí)約,而在使用多線程個(gè)線程的情況下,總共耗時(shí)約秒,效率整整提高了約倍。 爬蟲項(xiàng)目介紹 ??本次爬蟲項(xiàng)目將爬取豆瓣Top250電影的圖片,其網(wǎng)址為:https://movie.douban.com/top250, 具體頁(yè)面如...

    shiyang6017 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<