4.2 進程

進程

相關概念

進程

同步/異步

阻塞/非阻塞

併發/並行

順序執行:你吃飯吃到一半,電話來了,你一直到吃完了之後纔去接,這就說明你不支持併發也不支持並行。
       併發:你吃飯吃到一半,電話來了,你停了下來接了電話,接完後繼續吃飯,這說明你支持併發。
       並行:你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持並行python

進程狀態與調度

(1)就緒(Ready)狀態git

當進程已分配到除CPU之外的全部必要的資源,只要得到處理機即可當即執行,這時的進程狀態稱爲就緒狀態。github

(2)執行/運行(Running)狀態當進程已得到處理機,其程序正在處理機上執行,此時的進程狀態稱爲執行狀態。併發

(3)阻塞(Blocked)狀態正在執行的進程,因爲等待某個事件發生而沒法執行時,便放棄處理機而處於阻塞狀態。引發進程阻塞的事件可有多種,例如,等待I/O完成、申請緩衝區不能知足、等待信件(信號)等。app

多進程

建立進程

  示例dom

from multiprocessing import Process
import time


def task(name):
    print('{} is running!'.format(name))
    time.sleep(3)
    print('{} is done!'.format(name))


# Windows開子進程要寫在__name__==__main__下
# 由於開子進程會從新加載父進程的內容
if __name__ == '__main__':
    # 建立一個Python中的進程對象
    p = Process(target=task, args=('t1', ))
    # p = Process(target=task, kwargs={'name': 't1'})
    p.start()  # 調用操做系統接口啓動一個進程執行命令
    print('--- 主進程 ----')
View Code

  多進程示例異步

from multiprocessing import Process
import time


def task(name):
    print('{} is running!'.format(name))
    time.sleep(3)
    print('{} is done!'.format(name))


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=task, args=(i, ))
        p.start()
    print('--- 主進程 ----')
View Code

   繼承方式建立多進程async

import os
from multiprocessing import Process


class MyProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        print(os.getpid())
        print('%s 正在和女主播聊天' %self.name)

p1=MyProcess('wupeiqi')
p2=MyProcess('yuanhao')
p3=MyProcess('nezha')

p1.start() #start會自動調用run
p2.start()
# p2.run()
p3.start()


p1.join()
p2.join()
p3.join()

print('主線程')
View Code

相關屬性

p.daemon:默認值爲False,若是設爲True,表明p爲後臺運行的守護進程,當p的父進程終止時,p也隨之終止,而且設定爲True後,p不能建立本身的新進程,必須在p.start()以前設置
p.name:進程的名稱
p.pid:進程的pid
p.exitcode:進程在運行時爲None、若是爲–N,表示被信號N結束(瞭解便可)
p.authkey:進程的身份驗證鍵,

   daemon 守護進程示例ide

from multiprocessing import Process
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == '__main__':
    p1=Process(target=foo)
    p2=Process(target=bar)

    p1.daemon=True
    p1.start()
    p2.start()
    time.sleep(0.1)
    print("main-------")
    

"""
# 主進程結束了,守護進程隨着結束,致使p1 的 end123 沒法打印出來
123
456
main-------
end456
"""
View Code

相關方法

p.start():啓動進程,並調用該子進程中的p.run()
p.run():進程啓動時運行的方法,正是它去調用target指定的函數,咱們自定義類的類中必定要實現該方法
p.terminate():強制終止進程p,不會進行任何清理操做,若是p建立了子進程,該子進程就成了殭屍進程,使用該方法須要特別當心這種狀況。若是p還保存了一個鎖那麼也將不會被釋放,進而致使死鎖
p.is_alive():若是p仍然運行,返回True
p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,須要強調的是,p.join只能join住start開啓的進程,而不能join住run開啓的進程

   join 的示例函數

from multiprocessing import Process
import time


def task(n):
    print('這是子進程:{}'.format(n))
    time.sleep(n)
    print('子進程:{}結束了!'.format(n))


if __name__ == '__main__':
    start_time = time.time()
    p1 = Process(target=task, args=(1,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(3,))
    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()
    print('我是主進程')
    print('共耗時:{}'.format(time.time()-start_time))

"""
# 未加 join 主進程不等待其餘子進程程序結束便本身先行結束
我是主進程
共耗時:0.021976947784423828
這是子進程:1
這是子進程:2
這是子進程:3
子進程:1結束了!
子進程:2結束了!
子進程:3結束了!
"""

"""
# 加 join 主進程等待其餘子進程程序結束後再結束
這是子進程:1
這是子進程:2
這是子進程:3
子進程:1結束了!
子進程:2結束了!
子進程:3結束了!
我是主進程
共耗時:3.130025625228882
"""
View Code

進程互斥鎖

  洗手間模型演示無鎖致使的問題

from multiprocessing import Process
import time
import random


def task1():
    print('這是 task1 任務'.center(30, '-'))
    print('task1 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task1 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task1 走出了洗手間')


def task2():
    print('這是 task2 任務'.center(30, '-'))
    print('task2 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task2 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task2 走出了洗手間')


def task3():
    print('這是 task3 任務'.center(30, '-'))
    print('task3 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task3 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task3 走出了洗手間')


if __name__ == '__main__':
    p1 = Process(target=task1)
    p2 = Process(target=task2)
    p3 = Process(target=task3)

    p1.start()
    p2.start()
    p3.start()

"""
# 洗手間只能進一我的,尼瑪大家.....
---------這是 task1 任務----------
task1 進了洗手間
---------這是 task2 任務----------
task2 進了洗手間
---------這是 task3 任務----------
task3 進了洗手間
task2 辦事呢...
task1 辦事呢...
task3 辦事呢...
task1 走出了洗手間
task2 走出了洗手間
task3 走出了洗手間
"""
View Code

  加鎖解決示例

from multiprocessing import Process, Lock
import time
import random

# 生成一個互斥鎖
mutex_lock = Lock()


def task1(lock):
    # 鎖門
    lock.acquire()
    print('這是 task1 任務'.center(30, '-'))
    print('task1 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task1 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task1 走出了洗手間')
    # 釋放鎖
    lock.release()


def task2(lock):
    # 鎖門
    lock.acquire()
    print('這是 task2 任務'.center(30, '-'))
    print('task2 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task2 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task2 走出了洗手間')
    # 釋放鎖
    lock.release()


def task3(lock):
    # 鎖門
    lock.acquire()
    print('這是 task3 任務'.center(30, '-'))
    print('task3 進了洗手間')
    time.sleep(random.randint(1, 3))
    print('task3 辦事呢...')
    time.sleep(random.randint(1, 3))
    print('task3 走出了洗手間')
    # 釋放鎖
    lock.release()


if __name__ == '__main__':
    p1 = Process(target=task1, args=(mutex_lock, ))
    p2 = Process(target=task2, args=(mutex_lock, ))
    p3 = Process(target=task3, args=(mutex_lock, ))

    # 釋放新建進程的信號,具體誰先啓動沒法肯定
    p1.start()
    p2.start()
    p3.start()

"""
---------這是 task2 任務----------
task2 進了洗手間
task2 辦事呢...
task2 走出了洗手間
---------這是 task1 任務----------
task1 進了洗手間
task1 辦事呢...
task1 走出了洗手間
---------這是 task3 任務----------
task3 進了洗手間
task3 辦事呢...
task3 走出了洗手間
"""
View Code

   加鎖的弊端

進程池 

建立進程池

相關方法

p.apply(func [, args [, kwargs]])
  在一個池工做進程中執行func(
*args,**kwargs),而後返回結果。   須要強調的是:此操做並不會在全部池工做進程中並執行func函數。     若是要經過不一樣參數併發地執行func函數,必須從不一樣線程調用p.apply()函數或者使用p.apply_async() p.apply_async(func [, args [, kwargs[callback,]]])
  在一個池工做進程中執行func(
*args,**kwargs),而後返回結果
  callback是可調用對象,接收輸入參數。當func的結果變爲可用時, 將傳遞給callback   callback禁止執行任何阻塞操做,不然將接收其餘異步操做中的結果。 p.close()   關閉進程池,防止進一步操做   禁止往進程池內在添加任務(須要注意的是必定要寫在close()的上方) p.join()   等待全部工做進程退出。   此方法只能在close()或teminate()以後調用

相關實例

  穿行進程池實例
from multiprocessing import Pool
import os,time
def task(n):
    print('[%s] is running'%os.getpid())
    time.sleep(2)
    print('[%s] is done'%os.getpid())
    return n**2
if __name__ == '__main__':
    # print(os.cpu_count())  #查看cpu個數
    p = Pool(4) #最大四個進程
    for i in range(1,7):#開7個任務
        res = p.apply(task,args=(i,))  #同步的,等着一個運行完才執行另外一個
        print('本次任務的結束:%s'%res)
    p.close()#禁止往進程池內在添加任務
    p.join() #在等進程池
    print('')

apply同步進程池(阻塞)(串行)
View Code

   並行進程池實例

from multiprocessing import Pool
import os,time
def walk(n):
    print('task[%s] running...'%os.getpid())
    time.sleep(3)
    return n**2
if __name__ == '__main__':
     p = Pool(4)
     res_obj_l = []
     for i in range(10):
         res = p.apply_async(walk,args=(i,))
         # print(res)  #打印出來的是對象
         res_obj_l.append(res)  #那麼如今拿到的是一個列表,怎麼獲得值呢?咱們用個.get方法
     p.close() #禁止往進程池裏添加任務
     p.join()
     # print(res_obj_l)
     print([obj.get() for obj in res_obj_l])  #這樣就獲得了
View Code

  回調函數何時用?(回調函數在爬蟲中最經常使用)

    造數據的很是耗時
    處理數據的時候不耗時

  帶有回調函數的實例

from  multiprocessing import Pool
import requests
import os
import time
def get_page(url):
    print('<%s> is getting [%s]' %(os.getpid(),url))
    response = requests.get(url)  #獲得地址
    time.sleep(2)
    print('<%s> is  done [%s]'%(os.getpid(),url))
    return {'url':url,'text':response.text}
def parse_page(res):
    '''解析函數'''
    print('<%s> parse [%s]'%(os.getpid(),res['url']))
    with open('db.txt','a') as f:
        parse_res = 'url:%s size:%s\n' %(res['url'],len(res['text']))
        f.write(parse_res)
if __name__ == '__main__':
    p = Pool(4)
    urls = [
        'https://www.baidu.com',
        'http://www.openstack.org',
        'https://www.python.org',
        'https://help.github.com/',
        'http://www.sina.com.cn/'
    ]
    for url in urls:
        obj = p.apply_async(get_page,args=(url,),callback=parse_page)
    p.close()
    p.join()
    print('',os.getpid())  #都不用.get()方法了

回調函數(下載網頁的小例子)
View Code

   無需回調函數的實例

from  multiprocessing import Pool
import requests
import os
def get_page(url):
    print('<%os> get [%s]' %(os.getpid(),url))
    response = requests.get(url)  #獲得地址  response響應
    return {'url':url,'text':response.text}
if __name__ == '__main__':
    p = Pool(4)
    urls = [
        'https://www.baidu.com',
        'http://www.openstack.org',
        'https://www.python.org',
        'https://help.github.com/',
        'http://www.sina.com.cn/'
    ]
    obj_l= []
    for url in urls:
        obj = p.apply_async(get_page,args=(url,))
        obj_l.append(obj)
    p.close()
    p.join()
    print([obj.get() for obj in obj_l])

下載網頁小例子(無需回調函數)
View Code
相關文章
相關標籤/搜索