日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python学习笔记:进程和线程(起)

發(fā)布時間:2025/3/15 python 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python学习笔记:进程和线程(起) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

最近在學習深度學習,已經(jīng)跑出了幾個模型,但Pyhton的基礎(chǔ)不夠扎實,因此,開始補習Python了,大家都推薦廖雪峰的課程,因此,開始了學習,但光學有沒有用,還要和大家討論一下,因此,寫下這些帖子,廖雪峰的課程連接在這里:廖雪峰
Python的相關(guān)介紹,以及它的歷史故事和運行機制,可以參見這篇:python介紹
Python的安裝可以參見這篇:Python安裝
Python的運行模式以及輸入輸出可以參見這篇:Python IO
Python的基礎(chǔ)概念介紹,可以參見這篇:Python 基礎(chǔ)
Python字符串和編碼的介紹,可以參見這篇:Python字符串與編碼
Python基本數(shù)據(jù)結(jié)構(gòu):list和tuple介紹,可以參見這篇:Python list和tuple
Python控制語句介紹:ifelse,可以參見這篇:Python 條件判斷
Python控制語句介紹:循環(huán)實現(xiàn),可以參見這篇:Python循環(huán)語句
Python數(shù)據(jù)結(jié)構(gòu):dict和set介紹Python數(shù)據(jù)結(jié)構(gòu)dict和set
Python函數(shù)相關(guān):Python函數(shù)
Python高階特性:Python高級特性
Python高階函數(shù):Python高階函數(shù)
Python匿名函數(shù):Python匿名函數(shù)
Python裝飾器:Python裝飾器
Python偏函數(shù):Python偏函數(shù)
Python模塊:Python模塊
Python面向?qū)ο缶幊?#xff08;1):Python面向?qū)ο?
Python面向?qū)ο缶幊?#xff08;2):Python面向?qū)ο?#xff08;2)
Python面向?qū)ο缶幊?#xff08;3):Python面向?qū)ο?#xff08;3)
Python面向?qū)ο缶幊?#xff08;4):Pyhton面向?qū)ο?#xff08;4)
Python面向?qū)ο蟾呒壘幊?#xff08;上):Python面向?qū)ο蟾呒壘幊?#xff08;上)
Python面向?qū)ο蟾呒壘幊?#xff08;中上):Python面向?qū)ο蟾呒壘幊?#xff08;中上)
Python面向?qū)ο蟾呒壘幊?#xff08;中下):Python面向?qū)ο蟾呒壘幊?#xff08;中下)
Python面向?qū)ο蟾呒壘幊?#xff08;完):Python面向?qū)ο蟾呒壘幊?#xff08;完)
Python錯誤調(diào)試(起):Python調(diào)試:起
Python錯誤調(diào)試(承):Python調(diào)試:承
Python錯誤調(diào)試(轉(zhuǎn)):Python調(diào)試:轉(zhuǎn)
Python錯誤調(diào)試(合):python調(diào)試:合
Python文件IO編程:Python文件IO
Python文件IO編程2:Python文件IO2
Python文件IO編程3:PYthon文件IO3
目錄:

  • 前言
  • 概念
    • 進程
    • 線程
  • 小結(jié)
    • 多進程
    • Multiprocessing
    • 進程間通信
  • 小結(jié)

概念

很多同學都聽說過,現(xiàn)代操作系統(tǒng)比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任務”的操作系統(tǒng)。
什么叫“多任務”呢?簡單地說,就是操作系統(tǒng)可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網(wǎng),一邊在聽MP3,一邊在用Word趕作業(yè),這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在后臺同時運行著,只是桌面上沒有顯示而已。
現(xiàn)在,多核CPU已經(jīng)非常普及了,即使過去的單核CPU,也可以執(zhí)行多任務。由于CPU執(zhí)行代碼都是順序執(zhí)行的,那么,單核CPU是怎么執(zhí)行多任務的呢?
答案就是操作系統(tǒng)輪流讓各個任務交替執(zhí)行,任務1執(zhí)行0.01秒,切換到任務2,任務2執(zhí)行0.01秒,再切換到任務3,執(zhí)行0.01秒……這樣反復執(zhí)行下去。表面上看,每個任務都是交替執(zhí)行的,但是,由于CPU的執(zhí)行速度實在是太快了,我們感覺就像所有任務都在同時執(zhí)行一樣。

真正的并行執(zhí)行多任務只能在多核CPU上實現(xiàn),但是,由于任務數(shù)量遠遠多于CPU的核心數(shù)量,所以,操作系統(tǒng)也會自動把很多任務輪流調(diào)度到每個核心上執(zhí)行。

進程

對于操作系統(tǒng)來說,一個任務就是一個進程(Process),比如打開一個瀏覽器就是啟動一個瀏覽器進程,打開一個記事本就啟動了一個記事本進程,打開兩個記事本就啟動了兩個記事本進程,打開一個Word就啟動了一個Word進程。

有些進程還不止同時干一件事,比如Word,它可以同時進行打字、拼寫檢查、打印等事情。在一個進程內(nèi)部,要同時干多件事,就需要同時運行多個“子任務”,我們把進程內(nèi)的這些“子任務”稱為線程(Thread)。

線程

由于每個進程至少要干一件事,所以,一個進程至少有一個線程。當然,像Word這種復雜的進程可以有多個線程,多個線程可以同時執(zhí)行,多線程的執(zhí)行方式和多進程是一樣的,也是由操作系統(tǒng)在多個線程之間快速切換,讓每個線程都短暫地交替運行,看起來就像同時執(zhí)行一樣。當然,真正地同時執(zhí)行多線程需要多核CPU才可能實現(xiàn)。

我們前面編寫的所有的Python程序,都是執(zhí)行單任務的進程,也就是只有一個線程。如果我們要同時執(zhí)行多個任務怎么辦?

有兩種解決方案:

一種是啟動多個進程,每個進程雖然只有一個線程,但多個進程可以一塊執(zhí)行多個任務。
還有一種方法是啟動一個進程,在一個進程內(nèi)啟動多個線程,這樣,多個線程也可以一塊執(zhí)行多個任務。
當然還有第三種方法,就是啟動多個進程,每個進程再啟動多個線程,這樣同時執(zhí)行的任務就更多了,當然這種模型更復雜,實際很少采用。
總結(jié)一下就是,多任務的實現(xiàn)有3種方式:

多進程模式;多線程模式;多進程+多線程模式。

同時執(zhí)行多個任務通常各個任務之間并不是沒有關(guān)聯(lián)的,而是需要相互通信和協(xié)調(diào),有時,任務1必須暫停等待任務2完成后才能繼續(xù)執(zhí)行,有時,任務3和任務4又不能同時執(zhí)行,所以,多進程和多線程的程序的復雜度要遠遠高于我們前面寫的單進程單線程的程序。

因為復雜度高,調(diào)試困難,所以,不是迫不得已,我們也不想編寫多任務。但是,有很多時候,沒有多任務還真不行。想想在電腦上看電影,就必須由一個線程播放視頻,另一個線程播放音頻,否則,單線程實現(xiàn)的話就只能先把視頻播放完再播放音頻,或者先把音頻播放完再播放視頻,這顯然是不行的。

Python既支持多進程,又支持多線程,我們會討論如何編寫這兩種多任務程序。

小結(jié)

線程是最小的執(zhí)行單元,而進程由至少一個線程組成。如何調(diào)度進程和線程,完全由操作系統(tǒng)決定,程序自己不能決定什么時候執(zhí)行,執(zhí)行多長時間。
多進程和多線程的程序涉及到同步、數(shù)據(jù)共享的問題,編寫起來更復雜。

多進程

要讓Python程序?qū)崿F(xiàn)多進程(multiprocessing),我們先了解操作系統(tǒng)的相關(guān)知識。
Unix/Linux操作系統(tǒng)提供了一個fork()系統(tǒng)調(diào)用,它非常特殊。普通的函數(shù)調(diào)用,調(diào)用一次,返回一次,但是fork()調(diào)用一次,返回兩次,因為操作系統(tǒng)自動把當前進程(稱為父進程)復制了一份(稱為子進程),然后,分別在父進程和子進程內(nèi)返回。
子進程永遠返回0,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調(diào)用getppid()就可以拿到父進程的ID。
Python的os模塊封裝了常見的系統(tǒng)調(diào)用,其中就包括fork,可以在Python程序中輕松創(chuàng)建子進程:

import osprint('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0:print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else:print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

運行結(jié)果如下:

Process (876) start... I (876) just created a child process (877). I am child process (877) and my parent is 876.

由于Windows沒有fork調(diào)用,上面的代碼在Windows上無法運行。由于Mac系統(tǒng)是基于BSD(Unix的一種)內(nèi)核,所以,在Mac下運行是沒有問題的,推薦大家用Mac學Python!

有了fork調(diào)用,一個進程在接到新任務時就可以復制出一個子進程來處理新任務,常見的Apache服務器就是由父進程監(jiān)聽端口,每當有新的http請求時,就fork出子進程來處理新的http請求。

Multiprocessing

如果你打算編寫多進程的服務程序,Unix/Linux無疑是正確的選擇。由于Windows沒有fork調(diào)用,難道在Windows上無法用Python編寫多進程的程序?
由于Python是跨平臺的,自然也應該提供一個跨平臺的多進程支持。multiprocessing模塊就是跨平臺版本的多進程模塊。
multiprocessing模塊提供了一個Process類來代表一個進程對象,下面的例子演示了啟動一個子進程并等待其結(jié)束:

from multiprocessing import Process import os # 子進程要執(zhí)行的代碼 def run_proc(name):print('Run child process %s (%s)...' % (name, os.getpid()))if __name__=='__main__':print('Parent process %s.' % os.getpid())p = Process(target=run_proc, args=('test',))print('Child process will start.')p.start()p.join()print('Child process end.')

執(zhí)行結(jié)果如下:

Parent process 928. Process will start. Run child process test (929)... Process end.

創(chuàng)建子進程時,只需要傳入一個執(zhí)行函數(shù)和函數(shù)的參數(shù),創(chuàng)建一個Process實例,用start()方法啟動,這樣創(chuàng)建進程比fork()還要簡單。

join()方法可以等待子進程結(jié)束后再繼續(xù)往下運行,通常用于進程間的同步。 Pool

如果要啟動大量的子進程,可以用進程池的方式批量創(chuàng)建子進程:

from multiprocessing import Pool import os, time, randomdef long_time_task(name):print('Run task %s (%s)...' % (name, os.getpid()))start = time.time()time.sleep(random.random() * 3)end = time.time()print('Task %s runs %0.2f seconds.' % (name, (end - start)))if __name__=='__main__':print('Parent process %s.' % os.getpid())p = Pool(4)for i in range(5):p.apply_async(long_time_task, args=(i,))print('Waiting for all subprocesses done...')p.close()p.join()print('All subprocesses done.')

執(zhí)行結(jié)果如下:

Parent process 669. Waiting for all subprocesses done... Run task 0 (671)... Run task 1 (672)... Run task 2 (673)... Run task 3 (674)... Task 2 runs 0.14 seconds. Run task 4 (673)... Task 1 runs 0.27 seconds. Task 3 runs 0.86 seconds. Task 0 runs 1.41 seconds. Task 4 runs 1.91 seconds. All subprocesses done.

代碼解讀:

對Pool對象調(diào)用join()方法會等待所有子進程執(zhí)行完畢,調(diào)用join()之前必須先調(diào)用close(),調(diào)用close()之后就不能繼續(xù)添加新的Process了。

請注意輸出的結(jié)果,task 0,1,2,3是立刻執(zhí)行的,而task 4要等待前面某個task完成后才執(zhí)行,這是因為Pool的默認大小在我的電腦上是4,因此,最多同時執(zhí)行4個進程。這是Pool有意設(shè)計的限制,并不是操作系統(tǒng)的限制。如果改成:

p = Pool(5)

就可以同時跑5個進程。

由于Pool的默認大小是CPU的核數(shù),如果你不幸擁有8核CPU,你要提交至少9個子進程才能看到上面的等待效果。
子進程

很多時候,子進程并不是自身,而是一個外部進程。我們創(chuàng)建了子進程后,還需要控制子進程的輸入和輸出。

subprocess模塊可以讓我們非常方便地啟動一個子進程,然后控制其輸入和輸出。

下面的例子演示了如何在Python代碼中運行命令nslookup www.python.org,這和命令行直接運行的效果是一樣的:

import subprocessprint('$ nslookup www.python.org') r = subprocess.call(['nslookup', 'www.python.org']) print('Exit code:', r)

運行結(jié)果:

$ nslookup www.python.org Server: 192.168.19.4 Address: 192.168.19.4#53Non-authoritative answer: www.python.org canonical name = python.map.fastly.net. Name: python.map.fastly.net Address: 199.27.79.223Exit code: 0

如果子進程還需要輸入,則可以通過communicate()方法輸入:

import subprocessprint('$ nslookup') p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p.communicate(b'set q=mx\npython.org\nexit\n') print(output.decode('utf-8')) print('Exit code:', p.returncode)

上面的代碼相當于在命令行執(zhí)行命令nslookup,然后手動輸入:

set q=mx python.org exit

運行結(jié)果如下:

$ nslookup Server: 192.168.19.4 Address: 192.168.19.4#53Non-authoritative answer: python.org mail exchanger = 50 mail.python.org.Authoritative answers can be found from: mail.python.org internet address = 82.94.164.166 mail.python.org has AAAA address 2001:888:2000:d::a6Exit code: 0

進程間通信

Process之間肯定是需要通信的,操作系統(tǒng)提供了很多機制來實現(xiàn)進程間的通信。Python的multiprocessing模塊包裝了底層的機制,提供了Queue、Pipes等多種方式來交換數(shù)據(jù)。

我們以Queue為例,在父進程中創(chuàng)建兩個子進程,一個往Queue里寫數(shù)據(jù),一個從Queue里讀數(shù)據(jù):

from multiprocessing import Process, Queue import os, time, random# 寫數(shù)據(jù)進程執(zhí)行的代碼: def write(q):print('Process to write: %s' % os.getpid())for value in ['A', 'B', 'C']:print('Put %s to queue...' % value)q.put(value)time.sleep(random.random())# 讀數(shù)據(jù)進程執(zhí)行的代碼: def read(q):print('Process to read: %s' % os.getpid())while True:value = q.get(True)print('Get %s from queue.' % value)if __name__=='__main__':# 父進程創(chuàng)建Queue,并傳給各個子進程:q = Queue()pw = Process(target=write, args=(q,))pr = Process(target=read, args=(q,))# 啟動子進程pw,寫入:pw.start()# 啟動子進程pr,讀取:pr.start()# 等待pw結(jié)束:pw.join()# pr進程里是死循環(huán),無法等待其結(jié)束,只能強行終止:pr.terminate()

運行結(jié)果如下:

Process to write: 50563 Put A to queue... Process to read: 50564 Get A from queue. Put B to queue... Get B from queue. Put C to queue... Get C from queue.

在Unix/Linux下,multiprocessing模塊封裝了fork()調(diào)用,使我們不需要關(guān)注fork()的細節(jié)。由于Windows沒有fork調(diào)用,因此,multiprocessing需要“模擬”出fork的效果,父進程所有Python對象都必須通過pickle序列化再傳到子進程去,所有,如果multiprocessing在Windows下調(diào)用失敗了,要先考慮是不是pickle失敗了。

小結(jié)

在Unix/Linux下,可以使用fork()調(diào)用實現(xiàn)多進程。

要實現(xiàn)跨平臺的多進程,可以使用multiprocessing模塊。

進程間通信是通過Queue、Pipes等實現(xiàn)的。

總結(jié)

以上是生活随笔為你收集整理的Python学习笔记:进程和线程(起)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。