最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解Python 多線程

 更新時(shí)間:2020年06月16日 16:44:11   作者:追風(fēng)的小螞蟻  
這篇文章主要介紹了Python 多線程的相關(guān)知識(shí),文中講解的非常詳細(xì),代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

Python里的多線程是假的多線程,不管有多少核,同一時(shí)間只能在一個(gè)核中進(jìn)行操作!利用Python的多線程,只是利用CPU上下文切換的優(yōu)勢(shì),看上去像是并發(fā),其實(shí)只是個(gè)單線程,所以說他是假的單線程。

那么什么時(shí)候用多線程呢?

首先要知道:

  • io操作不占用CPU
  • 計(jì)算操作占CPU,像2+5=5

Python的多線程不適合CPU密集操作型的任務(wù),適合io密集操作型的任務(wù),例如:SocketServer

如果現(xiàn)在再有CPU密集操作型的任務(wù),那該怎么辦呢?

首先說,多進(jìn)程的進(jìn)程之間是獨(dú)立的,然后注意了,python的線程用的是系統(tǒng)的原生線程,python的進(jìn)程也是用系統(tǒng)的原生進(jìn)程,那原生進(jìn)程是由操作系統(tǒng)維護(hù)的,說白了python只是利用C原生代碼庫的接口咵嚓起了個(gè)進(jìn)程,真正的進(jìn)程管理還是由操作系統(tǒng)來完成的,那么操作系統(tǒng)本身有GIL全局解釋器鎖嗎?答案是沒有的,且兩個(gè)進(jìn)程之間的數(shù)據(jù)是完全獨(dú)立的,不能互相訪問,所以不需要鎖的概念,所以不存在GIL概念,所以在這種情況下,每個(gè)進(jìn)程至少會(huì)有一個(gè)線程,如果現(xiàn)在我的操作系統(tǒng)是八核的,我起八個(gè)進(jìn)程,然后每個(gè)進(jìn)程里面都有一個(gè)線程,那么就相當(dāng)于八線程了,八個(gè)線程跑在八核上,那么就相當(dāng)于利用多核了,那么問題就解決了!

唯一的壞處是八個(gè)線程之間的數(shù)據(jù)是不能共享的,獨(dú)立的!利用這種方法可以折中的解決多核運(yùn)算的問題!

先看一段簡(jiǎn)單的多進(jìn)程的程序:

import multiprocessing
import time

def run(name):
 time.sleep(2)
 print('hello', name)

if __name__ == '__main__':
 for i in range(10):
  p = multiprocessing.Process(target=run, args=('bob%s'%i,))
  p.start()

程序的執(zhí)行結(jié)果為:

hello bob0
hello bob1
hello bob3
hello bob2
hello bob5
hello bob9
hello bob7
hello bob8
hello bob4
hello bob6

那么,如果我想取我的進(jìn)程號(hào),那該怎么取呢?

from multiprocessing import Process
import os

def info(title):
 print(title)
 print('module name:', __name__)
 print('parent process:', os.getppid()) # 父進(jìn)程ID 
 print('process id:', os.getpid()) # 自己進(jìn)程的ID
 print("\n\n")

def f(name):
 info('\033[31;1mfunction f\033[0m')
 print('hello', name)

if __name__ == '__main__':
 info('\033[32;1mmain process line\033[0m')
 p = Process(target=f, args=('bob',))
 p.start()
 p.join()

程序執(zhí)行的結(jié)果為:

main process line
module name: __main__
parent process: 5252
process id: 6576

function f
module name: __mp_main__
parent process: 6576
process id: 2232

hello bob

其實(shí)這幅圖片的意思是,每一個(gè)子進(jìn)程都是由他父進(jìn)程啟動(dòng)的。

進(jìn)程間通訊

我們說兩個(gè)進(jìn)程之間的內(nèi)存之間是相互獨(dú)立的,那么這兩個(gè)進(jìn)程能夠進(jìn)行通信嗎?說A進(jìn)程向訪問B進(jìn)程的數(shù)據(jù),能訪問嗎?肯定是不可以訪問的!但是,我就是想訪問,也就是兩個(gè)獨(dú)立的內(nèi)存想互相訪問,那該怎么辦呢?

有那么幾種方式,但是呢!萬變不離其宗,也即是說你必須找到一個(gè)中間件,有那么幾種中間件,那么先來看看是哪幾種

第一種Queues

使用方法跟threading里的queue差不多

from multiprocessing import Process, Queue

def f(q):
 q.put([42, None, 'hello'])

if __name__ == '__main__':
 q = Queue()
 p = Process(target=f, args=(q,))
 p.start()
 print(q.get()) # prints "[42, None, 'hello']"
 p.join()

我們看這兩個(gè)進(jìn)程,父進(jìn)程的q是怎么傳給子進(jìn)程的?我們來討論一下

現(xiàn)在我們是不是認(rèn)為數(shù)據(jù)共享了,兩個(gè)進(jìn)程共享了一個(gè)q,其實(shí)不是的,其實(shí)是相當(dāng)于克隆了一個(gè)q,然后在父進(jìn)程里創(chuàng)建個(gè)子進(jìn)程,也就是父進(jìn)程把自己的q克隆了一份交給了子進(jìn)程,子進(jìn)程這個(gè)時(shí)候往這個(gè)q里面放了一份數(shù)據(jù),父進(jìn)程能夠獲取到 。那么這么說就不對(duì)了,那克隆了一個(gè)q,也就是兩個(gè)q了,B往q里放了一個(gè)數(shù)據(jù),那么與另一個(gè)q,也就是A的q也就沒關(guān)系了,噯,按說是這個(gè)樣子的,但是實(shí)際上呢,它是不是想實(shí)現(xiàn)個(gè)數(shù)據(jù)的共享啊,就相當(dāng)于把A這個(gè)q里的數(shù)據(jù)序列化了,序列化到了一個(gè)中間的位置,而中間位置有一個(gè)翻譯,他把這個(gè)數(shù)據(jù)反序列化給A,放在了A的q里,那么也就是實(shí)現(xiàn)了所謂的數(shù)據(jù)共享了。

程序執(zhí)行的結(jié)果為:

[42, None, 'hello']

第二種Pipes

Pipe()函數(shù)返回一個(gè)由管道連接的連接對(duì)象,默認(rèn)情況下是雙工(雙向)。 例如:

from multiprocessing import Process, Pipe

def f(conn):
 conn.send("父親,安好?") # 兒子發(fā)
 print("son receive:",conn.recv())
 conn.close()


if __name__ == '__main__':
 parent_conn, child_conn = Pipe()
 p = Process(target=f, args=(child_conn,))
 p.start()
 print("father receive:",parent_conn.recv()) # 父親收
 parent_conn.send("兒子,安好?")
 p.join()

程序執(zhí)行后的結(jié)果為:

father receive: 父親,安好?
son receive: 兒子,安好?

Pipe()返回的兩個(gè)連接對(duì)象代表管道的兩端。 每個(gè)連接對(duì)象都有send()和recv()方法(以及其他方法)。 請(qǐng)注意,如果兩個(gè)進(jìn)程(或線程)同時(shí)嘗試讀取或?qū)懭牍艿赖耐欢?,則管道中的數(shù)據(jù)可能會(huì)損壞。 當(dāng)然,同時(shí)使用管道的不同端部的過程不存在損壞的風(fēng)險(xiǎn)。

第三種Managers

Manager()返回的管理器對(duì)象控制一個(gè)服務(wù)器進(jìn)程,該進(jìn)程保存Python對(duì)象并允許其他進(jìn)程使用代理操作它們。

Manager()返回的管理器將支持類型列表,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Barrier,Queue,Value和Array。 例如,

from multiprocessing import Process, Manager
import os

def f(d, l):
 d[1] = '1'
 d['2'] = 2
 d[0.25] = None
 l.append(os.getpid())
 print(l)

if __name__ == '__main__':
 with Manager() as manager:
  d = manager.dict() # 用專門的語法生成一個(gè)可在多個(gè)進(jìn)程之間進(jìn)行傳遞和共享的一個(gè)字典

  l = manager.list(range(5)) # # 用專門的語法生成一個(gè)可在多個(gè)進(jìn)程之間進(jìn)行傳遞和共享的一個(gè)列表,默認(rèn)里有5個(gè)數(shù)據(jù)
  p_list = []
  for i in range(10):
   p = Process(target=f, args=(d, l))
   p.start()
   p_list.append(p)
  for res in p_list:
   res.join()
  print(d)
  print(l)

程序執(zhí)行的結(jié)果為:

[0, 1, 2, 3, 4, 2100]
[0, 1, 2, 3, 4, 2100, 7632]
[0, 1, 2, 3, 4, 2100, 7632, 5788]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888, 7612]
{1: '1', '2': 2, 0.25: None}
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888, 7612]

進(jìn)程鎖與進(jìn)程池

進(jìn)程鎖

進(jìn)程也有一個(gè)鎖,what?進(jìn)程不都獨(dú)立了嗎?不涉及同時(shí)修改同一個(gè)數(shù)據(jù),怎么還會(huì)有鎖呢?

閑了來看看它的表現(xiàn)形式,幾乎和線程是一模一樣的

from multiprocessing import Process, Lock

def f(l, i):
 l.acquire()
 try:
  print('hello world', i)
 finally:
  l.release()

if __name__ == '__main__':
 lock = Lock()
 for num in range(10):
  Process(target=f, args=(lock, num)).start()

程序執(zhí)行的結(jié)果為:

hello world 3
hello world 1
hello world 2
hello world 5
hello world 7
hello world 4
hello world 0
hello world 6
hello world 8
hello world 9

那這種鎖有什么作用呢?

作用其實(shí)就是防止打印在屏幕上的信息發(fā)生錯(cuò)亂現(xiàn)象!

進(jìn)程池

在上面的程序中,啟動(dòng)100個(gè)進(jìn)程會(huì)發(fā)現(xiàn)變慢了,因?yàn)槠鹨粋€(gè)進(jìn)程就相當(dāng)克隆了一份父進(jìn)程的內(nèi)存數(shù)據(jù),如果父進(jìn)程占一個(gè)G的內(nèi)存空間,那我起100個(gè)進(jìn)程,就相當(dāng)于101G了,在這種情況下,開銷是非常大的,就像起一個(gè)進(jìn)程咵嚓又克隆了一個(gè)屋子,一會(huì)就把哈爾濱占滿了,所以開銷特別大,為了避免咵嚓起那么多的進(jìn)程,把系統(tǒng)打趴下,所以這里有個(gè)進(jìn)程池的限制。

進(jìn)程池就是同一時(shí)間有多少進(jìn)程在CPU運(yùn)行。

進(jìn)程池中有兩個(gè)方法:

  • apply(同步執(zhí)行,串行)
  • apply_async(異步執(zhí)行、并行)
from multiprocessing import Process,Pool,freeze_support
import time
import os

def Foo(i):
 time.sleep(2)
 print("in process",os.getpid())
 return i+100

def Bar(arg):
 print('-->exec done:',arg)

if __name__ == '__main__':
 freeze_support()
 pool = Pool(5) # 允許進(jìn)程池里同時(shí)放入5個(gè)進(jìn)程

 for i in range(10):
  # pool.apply_async(func=Foo, args=(i,),callback=Bar) # callback 回調(diào)
  pool.apply(func=Foo, args=(i,)) # 串行
  # pool.apply_async(func=Foo, args=(i,)) # 并行

 print('end')

 pool.close()
 pool.join() # 進(jìn)程池中進(jìn)程執(zhí)行完畢后再關(guān)閉,如果注釋,那么程序直接關(guān)閉。

程序的執(zhí)行結(jié)果為:

in process 7824
in process 6540
in process 7724
in process 8924
in process 9108
in process 7824
in process 6540

知識(shí)點(diǎn)擴(kuò)充:

__name__  ==  '__main__'的作用是:

手動(dòng)執(zhí)行關(guān)于這段代碼的程序,那么他下面的程序就會(huì)執(zhí)行,如果是調(diào)用這段代碼的程序時(shí),那么它下面的程序就不會(huì)執(zhí)行

以上就是深入理解Python 多線程的詳細(xì)內(nèi)容,更多關(guān)于Python 多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

香河县| 崇左市| 固安县| 营山县| 虞城县| 会同县| 房山区| 勃利县| 纳雍县| 山东省| 天台县| 天台县| 惠水县| 乐昌市| 双峰县| 太谷县| 荣成市| 佛冈县| 敖汉旗| 姚安县| 昌平区| 兴宁市| 时尚| 汉源县| 游戏| 南投县| 博罗县| 泸水县| 类乌齐县| 公主岭市| 芦山县| 中超| 蓬溪县| 渭源县| 略阳县| 从化市| 屏东县| 绩溪县| 濮阳县| 武清区| 龙海市|