一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語(yǔ)言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - 深入理解 Python 中的多線程 新手必看

深入理解 Python 中的多線程 新手必看

2020-09-13 11:13Akshar Raaj Python

你應(yīng)當(dāng)將下邊的例子運(yùn)行多次,以便可以注意到線程是不可預(yù)測(cè)的和線程每次運(yùn)行出的不同結(jié)果。聲明:從這里開始忘掉你聽到過(guò)的關(guān)于GIL的東西,因?yàn)镚IL不會(huì)影響到我想要展示的東西

示例1
我們將要請(qǐng)求五個(gè)不同的url:
單線程

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time
import urllib2
 
defget_responses():
  urls=[
    ‘http://www.baidu.com',
    ‘http://www.amazon.com',
    ‘http://www.ebay.com',
    ‘http://www.alibaba.com',
  ]
  start=time.time()
  forurlinurls:
    printurl
    resp=urllib2.urlopen(url)
    printresp.getcode()
  print”Elapsed time: %s”%(time.time()-start)
 
get_responses()

輸出是:
http://www.baidu.com200
http://www.amazon.com200
http://www.ebay.com200
http://www.alibaba.com200
http://www.ythuaji.com.cn200
Elapsed time:3.0814409256

解釋:
url順序的被請(qǐng)求
除非cpu從一個(gè)url獲得了回應(yīng),否則不會(huì)去請(qǐng)求下一個(gè)url
網(wǎng)絡(luò)請(qǐng)求會(huì)花費(fèi)較長(zhǎng)的時(shí)間,所以cpu在等待網(wǎng)絡(luò)請(qǐng)求的返回時(shí)間內(nèi)一直處于閑置狀態(tài)。
多線程

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import urllib2
import time
from threading import Thread
 
classGetUrlThread(Thread):
  def__init__(self, url):
    self.url=url
    super(GetUrlThread,self).__init__()
 
  defrun(self):
    resp=urllib2.urlopen(self.url)
    printself.url, resp.getcode()
 
defget_responses():
  urls=[
    ‘http://www.baidu.com',
    ‘http://www.amazon.com',
    ‘http://www.ebay.com',
    ‘http://www.alibaba.com',
    ‘https://www.ythuaji.com.cn'
  ]
  start=time.time()
  threads=[]
  forurlinurls:
    t=GetUrlThread(url)
    threads.append(t)
    t.start()
  fortinthreads:
    t.join()
  print”Elapsed time: %s”%(time.time()-start)
 
get_responses()

輸出:
http://www.ythuaji.com.cn200
http://www.baidu.com200
http://www.amazon.com200
http://www.alibaba.com200
http://www.ebay.com200
Elapsed time:0.689890861511

解釋:

意識(shí)到了程序在執(zhí)行時(shí)間上的提升
我們寫了一個(gè)多線程程序來(lái)減少cpu的等待時(shí)間,當(dāng)我們?cè)诘却粋€(gè)線程內(nèi)的網(wǎng)絡(luò)請(qǐng)求返回時(shí),這時(shí)cpu可以切換到其他線程去進(jìn)行其他線程內(nèi)的網(wǎng)絡(luò)請(qǐng)求。
我們期望一個(gè)線程處理一個(gè)url,所以實(shí)例化線程類的時(shí)候我們傳了一個(gè)url。
線程運(yùn)行意味著執(zhí)行類里的run()方法。
無(wú)論如何我們想每個(gè)線程必須執(zhí)行run()。
為每個(gè)url創(chuàng)建一個(gè)線程并且調(diào)用start()方法,這告訴了cpu可以執(zhí)行線程中的run()方法了。
我們希望所有的線程執(zhí)行完畢的時(shí)候再計(jì)算花費(fèi)的時(shí)間,所以調(diào)用了join()方法。
join()可以通知主線程等待這個(gè)線程結(jié)束后,才可以執(zhí)行下一條指令。
每個(gè)線程我們都調(diào)用了join()方法,所以我們是在所有線程執(zhí)行完畢后計(jì)算的運(yùn)行時(shí)間。

關(guān)于線程:

cpu可能不會(huì)在調(diào)用start()后馬上執(zhí)行run()方法。
你不能確定run()在不同線程建間的執(zhí)行順序。
對(duì)于單獨(dú)的一個(gè)線程,可以保證run()方法里的語(yǔ)句是按照順序執(zhí)行的。
這就是因?yàn)榫€程內(nèi)的url會(huì)首先被請(qǐng)求,然后打印出返回的結(jié)果。

實(shí)例2

我們將會(huì)用一個(gè)程序演示一下多線程間的資源競(jìng)爭(zhēng),并修復(fù)這個(gè)問(wèn)題。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from threading import Thread
 
#define a global variable
some_var=0
 
classIncrementThread(Thread):
  defrun(self):
    #we want to read a global variable
    #and then increment it
    globalsome_var
    read_value=some_var
    print”some_var in %s is %d”%(self.name, read_value)
    some_var=read_value+1
    print”some_var in %s after increment is %d”%(self.name, some_var)
 
defuse_increment_thread():
  threads=[]
  foriinrange(50):
    t=IncrementThread()
    threads.append(t)
    t.start()
  fortinthreads:
    t.join()
  print”After 50 modifications, some_var should have become 50
  print”After 50 modifications, some_var is %d”%(some_var,)
 
use_increment_thread()

多次運(yùn)行這個(gè)程序,你會(huì)看到多種不同的結(jié)果。
解釋:
有一個(gè)全局變量,所有的線程都想修改它。
所有的線程應(yīng)該在這個(gè)全局變量上加 1 。
有50個(gè)線程,最后這個(gè)數(shù)值應(yīng)該變成50,但是它卻沒(méi)有。
為什么沒(méi)有達(dá)到50?
在some_var是15的時(shí)候,線程t1讀取了some_var,這個(gè)時(shí)刻cpu將控制權(quán)給了另一個(gè)線程t2。
t2線程讀到的some_var也是15
t1和t2都把some_var加到16
當(dāng)時(shí)我們期望的是t1 t2兩個(gè)線程使some_var + 2變成17
在這里就有了資源競(jìng)爭(zhēng)。
相同的情況也可能發(fā)生在其它的線程間,所以出現(xiàn)了最后的結(jié)果小于50的情況。
解決資源競(jìng)爭(zhēng)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from threading import Lock, Thread
lock=Lock()
some_var=0
 
classIncrementThread(Thread):
  defrun(self):
    #we want to read a global variable
    #and then increment it
    globalsome_var
    lock.acquire()
    read_value=some_var
    print”some_var in %s is %d”%(self.name, read_value)
    some_var=read_value+1
    print”some_var in %s after increment is %d”%(self.name, some_var)
    lock.release()
 
defuse_increment_thread():
  threads=[]
  foriinrange(50):
    t=IncrementThread()
    threads.append(t)
    t.start()
  fortinthreads:
    t.join()
  print”After 50 modifications, some_var should have become 50
  print”After 50 modifications, some_var is %d”%(some_var,)
 
use_increment_thread()

再次運(yùn)行這個(gè)程序,達(dá)到了我們預(yù)期的結(jié)果。
解釋:
Lock 用來(lái)防止競(jìng)爭(zhēng)條件
如果在執(zhí)行一些操作之前,線程t1獲得了鎖。其他的線程在t1釋放Lock之前,不會(huì)執(zhí)行相同的操作
我們想要確定的是一旦線程t1已經(jīng)讀取了some_var,直到t1完成了修改some_var,其他的線程才可以讀取some_var
這樣讀取和修改some_var成了邏輯上的原子操作。
實(shí)例3
讓我們用一個(gè)例子來(lái)證明一個(gè)線程不能影響其他線程內(nèi)的變量(非全局變量)。
time.sleep()可以使一個(gè)線程掛起,強(qiáng)制線程切換發(fā)生。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from threading import Thread
import time
 
classCreateListThread(Thread):
  defrun(self):
    self.entries=[]
    foriinrange(10):
      time.sleep(1)
      self.entries.append(i)
    printself.entries
 
defuse_create_list_thread():
  foriinrange(3):
    t=CreateListThread()
    t.start()
 
use_create_list_thread()

運(yùn)行幾次后發(fā)現(xiàn)并沒(méi)有打印出爭(zhēng)取的結(jié)果。當(dāng)一個(gè)線程正在打印的時(shí)候,cpu切換到了另一個(gè)線程,所以產(chǎn)生了不正確的結(jié)果。我們需要確保print self.entries是個(gè)邏輯上的原子操作,以防打印時(shí)被其他線程打斷。
我們使用了Lock(),來(lái)看下邊的例子。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from threading import Thread, Lock
import time
 
lock=Lock()
 
classCreateListThread(Thread):
  defrun(self):
    self.entries=[]
    foriinrange(10):
      time.sleep(1)
      self.entries.append(i)
    lock.acquire()
    printself.entries
    lock.release()
 
defuse_create_list_thread():
  foriinrange(3):
    t=CreateListThread()
    t.start()
 
use_create_list_thread()

這次我們看到了正確的結(jié)果。證明了一個(gè)線程不可以修改其他線程內(nèi)部的變量(非全局變量)。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品视频日本 | 万域之王在线观看 | 男生操女生动态图 | 99久久精品免费看国产一区 | 九九九九九九伊人 | 欧美日韩亚洲高清不卡一区二区三区 | 成人夜视频寂寞在线观看 | 特级夫妻大片免费在线播放 | 人人爽人人香蕉 | 奇米影视奇米色777欧美 | 99er视频| 日本理论片中文在线观看2828 | youjizzxxx在线观看| 欧美一级欧美三级在线 | 爆操俄罗斯美女 | nhdta系列媚药系列 | 欧美精品一区二区在线观看播放 | 色亚| 亚洲入口 | 99re8在这里只有精品23 | 波多野结衣52部合集在线观看 | 国产成人精品免费午夜 | 国产成人亚洲影视在线 | 国产一区二区三区日韩 | 免费jizz在在线播放国产 | 四川女人偷人真实视频 | 国产伦久视频免费观看视频 | 操操久久| 国产老妇 | 黑人巨大videosjapan高清 黑人好大 | 日本高清免费看 | 跪趴好紧h| 亚洲伦理视频 | 百合女女师生play黄肉黄 | 精品国产免费一区二区三区 | 色先锋av资源中文字幕 | 久久精品视频在线看 | 公交车强校花系列小说 | 小小水蜜桃视频高清在线播放 | 国产精品久久久久久吹潮 | 精品久久久久久影院免费 |