App下載

Python進(jìn)程是什么?進(jìn)程知識(shí)點(diǎn)詳解

猿友 2021-07-19 17:35:53 瀏覽數(shù) (1943)
反饋

一、前言

進(jìn)程,一個(gè)新鮮的字眼,可能有些人并不了解,它是系統(tǒng)某個(gè)運(yùn)行程序的載體,這個(gè)程序可以有單個(gè)或者多個(gè)進(jìn)程,一般來(lái)說(shuō),進(jìn)程是通過(guò)系統(tǒng)CPU 內(nèi)核數(shù)來(lái)分配并設(shè)置的,我們可以來(lái)看下系統(tǒng)中的進(jìn)程:

進(jìn)程查看

可以看到,360瀏覽器是真的皮,這么多進(jìn)程啊,當(dāng)然可以這樣來(lái)十分清楚的看進(jìn)程線程使用情況:

進(jìn)程查看

通過(guò)任務(wù)管理器中的資源監(jiān)視器,是不是很厲害了,哈哈哈。講完了這些,再說(shuō)說(shuō)用法。

二、基本用法

進(jìn)程能干什么,這是我們要深思熟慮的事情。我們都知道一個(gè)程序運(yùn)行會(huì)創(chuàng)建進(jìn)程,所以程序在創(chuàng)建這些進(jìn)程的時(shí)候,為了讓它們更能有條不紊的工作,肯定也加入了線程。

那么一條進(jìn)程里面就會(huì)有多個(gè)線程協(xié)同作戰(zhàn),但是進(jìn)程不可以創(chuàng)建過(guò)多,不然會(huì)消耗資源,除非你開(kāi)發(fā)的是一個(gè)大型的系統(tǒng)。那么,我們現(xiàn)在就來(lái)創(chuàng)建一個(gè)進(jìn)程吧。

一、創(chuàng)建進(jìn)程 1.在創(chuàng)建進(jìn)程之前,我們先導(dǎo)入進(jìn)程的模塊,代碼如下:

import multiprocess as m
m.Process(target,args)

其實(shí)這種寫法是不對(duì)的,就好比bs4中的BeautifulSoup,你想通過(guò)先導(dǎo)入bs4,然后再引入BeautifulSoup是行不通的,必須這樣:

from multiprocessing import Process
Process(group, target, args, kwargs, name)
group:用戶組
target:調(diào)用函數(shù)
args:參數(shù)元祖
kwargs:參數(shù)字典
name:子進(jìn)程名稱

可以看出進(jìn)程和線程的用法基本差不多,只是名稱功能不同而已。而且還有很多其它優(yōu)秀的方法:

# 返回當(dāng)前進(jìn)程存活的子進(jìn)程的列表。調(diào)用該方法有“等待”已經(jīng)結(jié)束的進(jìn)程的副作用。
multiprocessing.active_children()
 
 
# 返回系統(tǒng)的CPU數(shù)量。
multiprocessing.cpu_count()

三、創(chuàng)建單個(gè)進(jìn)程

由上述參數(shù)可知函數(shù)的返回值,基本與線程無(wú)差異化。

#啟動(dòng)進(jìn)程,調(diào)用進(jìn)程中的run()方法。
start()
 
 
#進(jìn)程活動(dòng)的方法
run()
 
 
#強(qiáng)制終止進(jìn)程,不會(huì)進(jìn)行任何清理操作。如果終止前創(chuàng)建了子進(jìn)程,那么該子進(jìn)程在其強(qiáng)制結(jié)束后變?yōu)榻┦M(jìn)程;如果該進(jìn)程還保存了一個(gè)鎖,那么也將不會(huì)被釋放,進(jìn)而導(dǎo)致死鎖。
terminate()
 
 
#判斷某進(jìn)程是否存活,存活返回True,否則False。
is_alive()
 
 
主線程等待子線程終止。timeout為可選擇超時(shí)時(shí)間;需要強(qiáng)調(diào)的是:p.join只能join住start開(kāi)啟的進(jìn)程,而不能join住run開(kāi)啟的進(jìn)程。
join([timeout])
 
 
#設(shè)置進(jìn)程為后臺(tái)守護(hù)進(jìn)程;當(dāng)該進(jìn)程的父進(jìn)程終止時(shí),該進(jìn)程也隨之終止,并且該進(jìn)程不能創(chuàng)建子進(jìn)程,設(shè)置該屬性必須在start()之前
daemon
 
 
#進(jìn)程名稱。
name
 
 
#進(jìn)程pid,在start后才能產(chǎn)生
pid
 
 
#子進(jìn)程的退出代碼。如果進(jìn)程尚未終止,這將是 None,負(fù)值-N表示子進(jìn)程被信號(hào)N終止。
exitcode
 
 
#進(jìn)程身份驗(yàn)證,默認(rèn)是os.urandom()隨機(jī)生成的字符串。校驗(yàn)網(wǎng)進(jìn)程連接是否正確
authkey
 
 
#系統(tǒng)對(duì)象的數(shù)字句柄,當(dāng)進(jìn)程結(jié)束時(shí)將變?yōu)?"ready" 。
sentinel
 
 
#殺進(jìn)程
kill()
 
 
#關(guān)閉進(jìn)程
close()

請(qǐng)注意:創(chuàng)建進(jìn)程務(wù)必將它加入如下語(yǔ)句中:

if __name__ == '__main__':

進(jìn)程創(chuàng)建

這樣就實(shí)現(xiàn)了我們的一個(gè)關(guān)于進(jìn)程的程序了。另外我們也可以通過(guò)繼承進(jìn)程類來(lái)實(shí)現(xiàn):

進(jìn)程繼承

可以說(shuō)我們每創(chuàng)建一個(gè)進(jìn)程它就會(huì)有一個(gè)ID來(lái)標(biāo)志它,下面情況:

進(jìn)程ID

四、創(chuàng)建多個(gè)進(jìn)程

單個(gè)進(jìn)程往往都是不夠用的,所有我們需要?jiǎng)?chuàng)建一個(gè)多進(jìn)程,多進(jìn)程創(chuàng)建方法也很簡(jiǎn)單,加一層循環(huán)即可:

創(chuàng)建多個(gè)進(jìn)程

這樣就輕松創(chuàng)建了多進(jìn)程的任務(wù),速度比以往就要更快了。

五、進(jìn)程池

進(jìn)程池的設(shè)計(jì)之初就是為了方便我們更有效的利用資源,避免浪費(fèi),如果任務(wù)量大就多個(gè)核一起幫忙,如果少就只開(kāi)一兩個(gè)核,下面我們來(lái)看看實(shí)現(xiàn)過(guò)程:

首先導(dǎo)入包:

from multiprocessing import Pool
import multiprocessing as m

進(jìn)程池的安裝包為Pool,然后我們來(lái)看下它的CPU內(nèi)核數(shù):

num=m.cpu_count()#CPU內(nèi)核數(shù)

緊接著我們?cè)趤?lái)創(chuàng)建進(jìn)程池:

pool=multiprocessing.Pool(num)

進(jìn)程池中也有很多方法供我們使用:

apply(func,args,kwargs)                 同步執(zhí)行(串行) 阻塞
 
 
apply_async(func,args,kwargs)           異步執(zhí)行(并行) 非阻塞
 
 
terminate()            強(qiáng)制終止進(jìn)程,不在處理未完成的任務(wù)。
 
 
join()     主進(jìn)程阻塞,等待子進(jìn)程的退出。必須在close或terminate()之后使用
 
 
close()            等待所有進(jìn)程結(jié)束后,才關(guān)閉進(jìn)程池
 
 
map(func,iterable,chunksize=int) map函數(shù)的并行版本,保持阻塞直到獲得結(jié)果
 
 
#返回一個(gè)可用于獲取結(jié)果的對(duì)象,回調(diào)函數(shù)應(yīng)該立即執(zhí)行完成,否則會(huì)阻塞負(fù)責(zé)處理結(jié)果的線程
map_async(func,iterable,chunksize,callback,error_callback)
 
 
imap(func,iterable,chunksize) map的延遲執(zhí)行版本
 
 
#和imap() 相同,只不過(guò)通過(guò)迭代器返回的結(jié)果是任意的
imap_unordered(func,iterable,chunksize)
 
 
#和 map() 類似,不過(guò) iterable 中的每一項(xiàng)會(huì)被解包再作為函數(shù)參數(shù)。
starmap(func,iterable,chunksize)

為此我們可以創(chuàng)建同步和異步的程序,如果你對(duì)這對(duì)于爬蟲來(lái)說(shuō)是很不錯(cuò)的選擇,小點(diǎn)的爬蟲同步就好,大的爬蟲異步效果更佳,很多人不了解異步和同步,其實(shí)同步異步就是串行和并行的意思串行和并行簡(jiǎn)單點(diǎn)說(shuō)就是串聯(lián)和并聯(lián)。下面我們通過(guò)實(shí)例一起來(lái)看一下:

串行

串行

并行

并行

可以看到,僅僅只是一個(gè)參數(shù)的變化而已,其它的都是大同小異,我們獲取到了當(dāng)前進(jìn)程的pid,然后把它打印出來(lái)了。

六、鎖

雖然異步編程多進(jìn)程給我們帶來(lái)了便利,但是進(jìn)程啟動(dòng)后是不可控的,我們需要將它控制住,讓它干我們覺(jué)得有意義的事,這個(gè)時(shí)候我們需要給它加鎖,和線程一樣都是lock:

首先導(dǎo)入進(jìn)程鎖的模塊:

from multiprocessing import Lock

然后我們來(lái)創(chuàng)建一個(gè)關(guān)于鎖的程序:

進(jìn)程鎖

可以看到,加鎖的過(guò)程還是比較順利的,跟多線程一樣簡(jiǎn)單,但是相對(duì)來(lái)說(shuō)速度會(huì)慢一點(diǎn)。既然有Lock,那么勢(shì)必就有RLock了,在python 中,進(jìn)程和線程的很多用法一致,鎖就是。我們可以把它改為RLock,下面便是可重入鎖,也就是可以遞歸:

import time
lock1=RLock()
lock2=RLock()
s=time.time()
def jc(num):
    lock1.acquire()
    lock2.acquire()
    print('start')
    print(m.current_process().pid,'run----',str(num))
    lock1.release()
    lock2.release()
    print('end')
if __name__ == '__main__':
    aa=[]
    for y in range(12):
        pp=Process(target=jc,args=(y,))
        pp.start()
        aa.append(pp)
    for x in aa:
        x.join()
    e=time.time()
    print(e-s)

七、進(jìn)程間通信

Event

進(jìn)程間用于通信,方法和線程的一模一樣,這里舉個(gè)小栗子,不在詳細(xì)描述,不懂的可以看我上一篇關(guān)于線程的文章,我們今天要講的是其它的進(jìn)程間通信方式,下面請(qǐng)看:

import time
e=Event()
def main(num):
    while True:
        if num<5:
            e.clear()   #清空信號(hào)標(biāo)志
            print('清空')
        if num>=5:
            e.wait(timeout=1) #等待信號(hào)標(biāo)志為真
            e.set()
            print('啟動(dòng)')
            
        if num==10:
            e.wait(timeout=3)
            e.clear()
            print('退出')
            break
        num+=1
        time.sleep(2)
if __name__ == '__main__':
    for y in range(10):
        pp=Process(target=main,args=(y,))
        pp.start()
        pp.join()

管道傳遞消息

管道模塊初始化后返回兩個(gè)參數(shù),一個(gè)為發(fā)送者,一個(gè)為接收者,它有個(gè)參數(shù)可以設(shè)置模式為全雙工或者半雙工,全雙工收發(fā)一體,半雙工只收或者只發(fā),先了解下它的方法:

p1,p2=m.Pipe(duplex=bool) #設(shè)置是否全雙工,返回兩個(gè)連接對(duì)象
p1.send()  #發(fā)送
p2.recv()  #接收
p1.close()  #關(guān)閉連接
p1.fileno() #返回連接使用的整數(shù)文件描述符
p1.poll([timeout]) #如果連接上的數(shù)據(jù)可用,返回True,timeout指定等待的最長(zhǎng)時(shí)限。
p2.recv_bytes([maxlength]) #接收最大字節(jié)數(shù)
p1.send_bytes([maxlength]) #發(fā)送最大字節(jié)數(shù)
#接收一條完整的字節(jié)消息,并把它保存在buffer對(duì)象中,offset指定緩沖區(qū)中放置消息處的字節(jié)位移.
p2.recv_bytes_into(buffer [, offset])

管道通信

先收后發(fā),其實(shí)我們完全可以使用鎖來(lái)控制它的首發(fā),可以讓它一邊收一邊發(fā)。

隊(duì)列

隊(duì)列與其它不同的是它采取插入和刪除的方法,讓我們來(lái)看下:

def fd(a):
    for y in range(10):
        a.put(y)  #插入數(shù)據(jù)
        print('插入:',str(y))
def df(b):
    while True:
        aa=b.get(True) #刪除數(shù)據(jù)
        print('釋放:',str(aa))
if __name__ == '__main__':
    q=Queue()
    ff=Process(target=fd,args=(q,))
    dd=Process(target=df,args=(q,))
    ff.start() #開(kāi)始運(yùn)行
    dd.start()
    dd.terminate() #關(guān)閉
    ff.join()

以上講的隊(duì)列主要用于多進(jìn)程的隊(duì)列,還有一個(gè)進(jìn)程池的隊(duì)列,它在Manager模塊中。

八、信號(hào)量

與線程中完全一樣,這里不在贅述,看下例:

s=Semaphore(3)
s.acquire()
print(s.get_value())
s.release()
print(s.get_value())
print(s.get_value())
s.release()
print(s.get_value())
s.release()
 
 
output:
2
3
3
4

九、數(shù)據(jù)共享

共享數(shù)據(jù)類型可以直接通過(guò)進(jìn)程模塊來(lái)設(shè)置:

數(shù)值型:m.Value()  
數(shù)組性:m.Array()  
字典型:m.dict()  
列表型:m.list()

也可以通過(guò)進(jìn)程的Manager模塊來(lái)實(shí)現(xiàn):

Manager().dict()
Manager.list()

下面我們就來(lái)舉例說(shuō)明下吧:

數(shù)據(jù)共享

可以看到我們成功的將數(shù)據(jù)添加了進(jìn)去,形成了數(shù)據(jù)的共享。

十、總結(jié)

通過(guò)對(duì)進(jìn)程的描述,相信大家對(duì)進(jìn)程此刻有了個(gè)深刻的感悟了吧,突然想起個(gè)事,就是大家學(xué)習(xí)時(shí)可能查資料會(huì)在網(wǎng)上搜索,那么我建議你專心看好我這篇好了,因?yàn)閾?jù)我所知,那些都是錯(cuò)的,而且更讓我納悶的是,明明代碼是錯(cuò)的,放出來(lái)的執(zhí)行效果卻是對(duì)的,這讓我百思不得其解,哈哈哈。

到此這篇關(guān)于python進(jìn)程知識(shí)點(diǎn)的文章就介紹到這了,更多Python高階教程的內(nèi)容請(qǐng)搜索W3Cschool以前的文章或繼續(xù)瀏覽下面的相關(guān)文章。

0 人點(diǎn)贊