python线程与进程
生活随笔
收集整理的這篇文章主要介紹了
python线程与进程
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
建議用pycharm閱讀,可以收縮,也可以測(cè)試
'''
IO多路復(fù)用
? ?I/O多路復(fù)用指:通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫(xiě)就緒),能夠通知程
序進(jìn)行相應(yīng)的讀寫(xiě)操作。目前支持I/O多路復(fù)用的系統(tǒng)調(diào)用有 select,poll,epoll
應(yīng)用場(chǎng)景:
? ?服務(wù)器需要同時(shí)處理多個(gè)處于監(jiān)聽(tīng)狀態(tài)或者多個(gè)連接狀態(tài)的套接字。
? ?服務(wù)器需要同時(shí)處理多種網(wǎng)絡(luò)協(xié)議的套接字。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import select
import socket
import Queue
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , 1)
server_address= ('192.168.1.5',8080)
server.bind(server_address)
server.listen(10)
#select輪詢等待讀socket集合
inputs = [server]
#select輪詢等待寫(xiě)socket集合
outputs = []
message_queues = {}
#select超時(shí)時(shí)間
timeout = 20
while True:
? ?print "等待活動(dòng)連接......"
? ?readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)
? ?if not (readable or writable or exceptional) :
? ? ? ?print "select超時(shí)無(wú)活動(dòng)連接,重新select...... "
? ? ? ?continue;
? ?#循環(huán)可讀事件
? ?for s in readable :
? ? ? ?#如果是server監(jiān)聽(tīng)的socket
? ? ? ?if s is server:
? ? ? ? ? ?#同意連接
? ? ? ? ? ?connection, client_address = s.accept()
? ? ? ? ? ?print "新連接: ", client_address
? ? ? ? ? ?connection.setblocking(0)
? ? ? ? ? ?#將連接加入到select可讀事件隊(duì)列
? ? ? ? ? ?inputs.append(connection)
? ? ? ? ? ?#新建連接為key的字典,寫(xiě)回讀取到的消息
? ? ? ? ? ?message_queues[connection] = Queue.Queue()
? ? ? ?else:
? ? ? ? ? ?#不是本機(jī)監(jiān)聽(tīng)就是客戶端發(fā)來(lái)的消息
? ? ? ? ? ?data = s.recv(1024)
? ? ? ? ? ?if data :
? ? ? ? ? ? ? ?print "收到數(shù)據(jù):" , data , "客戶端:",s.getpeername()
? ? ? ? ? ? ? ?message_queues[s].put(data)
? ? ? ? ? ? ? ?if s not in outputs:
? ? ? ? ? ? ? ? ? ?#將讀取到的socket加入到可寫(xiě)事件隊(duì)列
? ? ? ? ? ? ? ? ? ?outputs.append(s)
? ? ? ? ? ?else:
? ? ? ? ? ? ? ?#空白消息,關(guān)閉連接
? ? ? ? ? ? ? ?print "關(guān)閉連接:", client_address
? ? ? ? ? ? ? ?if s in outputs :
? ? ? ? ? ? ? ? ? ?outputs.remove(s)
? ? ? ? ? ? ? ?inputs.remove(s)
? ? ? ? ? ? ? ?s.close()
? ? ? ? ? ? ? ?del message_queues[s]
? ?for s in writable:
? ? ? ?try:
? ? ? ? ? ?msg = message_queues[s].get_nowait()
? ? ? ?except Queue.Empty:
? ? ? ? ? ?print "連接:" , s.getpeername() , '消息隊(duì)列為空'
? ? ? ? ? ?outputs.remove(s)
? ? ? ?else:
? ? ? ? ? ?print "發(fā)送數(shù)據(jù):" , msg , "到", s.getpeername()
? ? ? ? ? ?s.send(msg)
? ?for s in exceptional:
? ? ? ?print "異常連接:", s.getpeername()
? ? ? ?inputs.remove(s)
? ? ? ?if s in outputs:
? ? ? ? ? ?outputs.remove(s)
? ? ? ?s.close()
? ? ? ?del message_queues[s]
'''
'''
? ?進(jìn)程和線程的區(qū)別和關(guān)系:
? ?對(duì)于操作系統(tǒng)來(lái)說(shuō),一個(gè)任務(wù)就是一個(gè)進(jìn)程(Process),比如打開(kāi)一個(gè)瀏覽器就是啟動(dòng)一個(gè)瀏覽器進(jìn)程,打開(kāi)一個(gè)記事
本就啟動(dòng)了一個(gè)記事本進(jìn)程,打開(kāi)兩個(gè)記事本就啟動(dòng)了兩個(gè)記事本進(jìn)程,打開(kāi)一個(gè)Word就啟動(dòng)了一個(gè)Word進(jìn)程。
? ?有些進(jìn)程還不止同時(shí)干一件事,比如Word,它可以同時(shí)進(jìn)行打字、拼寫(xiě)檢查、打印等事情。在一個(gè)進(jìn)程內(nèi)部,要同時(shí)干多
件事,就需要同時(shí)運(yùn)行多個(gè)“子任務(wù)”,我們把進(jìn)程內(nèi)的這些“子任務(wù)”稱為線程(Thread)。
? ?由于每個(gè)進(jìn)程至少要干一件事,所以,一個(gè)進(jìn)程至少有一個(gè)線程。當(dāng)然,像Word這種復(fù)雜的進(jìn)程可以有多個(gè)線程,多個(gè)線
程可以同時(shí)執(zhí)行,多線程的執(zhí)行方式和多進(jìn)程是一樣的,也是由操作系統(tǒng)在多個(gè)線程之間快速切換,讓每個(gè)線程都短暫地交替
運(yùn)行,看起來(lái)就像同時(shí)執(zhí)行一樣。當(dāng)然,真正地同時(shí)執(zhí)行多線程需要多核CPU才可能實(shí)現(xiàn)。
? ?線程是最小的執(zhí)行單元,而進(jìn)程由至少一個(gè)線程組成。如何調(diào)度進(jìn)程和線程,完全由操作系統(tǒng)決定,程序自己不能決定什
么時(shí)候執(zhí)行,執(zhí)行多長(zhǎng)時(shí)間。
'''
'''
python的進(jìn)程
? ?multiprocessing包的組件Process, Queue, Pipe, Lock等組件提供了與多線程類似的功能。使用這些組件,可以方便
地編寫(xiě)多進(jìn)程并發(fā)程序。
'''
'''
Queue隊(duì)列
? ?Queue是多進(jìn)程安全的隊(duì)列,可以使用Queue實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)傳遞。put方法用以插入數(shù)據(jù)到隊(duì)列中,put方法還有
兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue(默認(rèn)值),并且timeout為正值,該方法會(huì)阻塞timeout指定的時(shí)
間,直到該隊(duì)列有剩余的空間。如果超時(shí),會(huì)拋出Queue.Full異常。如果blocked為False,但該Queue已滿,會(huì)立即拋出
Queue.Full異常。
? ?get方法可以從隊(duì)列讀取并且刪除一個(gè)元素。同樣,get方法有兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue
(默認(rèn)值),并且timeout為正值,那么在等待時(shí)間內(nèi)沒(méi)有取到任何元素,會(huì)拋出Queue.Empty異常。如果blocked為False,
有兩種情況存在,如果Queue有一個(gè)值可用,則立即返回該值,否則,如果隊(duì)列為空,則立即拋出Queue.Empty異常。
from multiprocessing import Process, Queue
def offer(queue):
? ?queue.put("Hello World")
if __name__ == '__main__':
? ?q = Queue()
? ?p = Process(target=offer, args=(q,))
? ?p.start()
? ?print q.get()
'''
'''
Pipes管道
? ?Pipe方法返回(conn1, conn2)代表一個(gè)管道的兩個(gè)端。Pipe方法有duplex參數(shù),如果duplex參數(shù)為T(mén)rue(默認(rèn)值)那么
這個(gè)管道是全雙工模式,也就是說(shuō)conn1和conn2均可收發(fā)。duplex為False,conn1只負(fù)責(zé)接受消息,conn2只負(fù)責(zé)發(fā)送消息
? ?send和recv方法分別是發(fā)送和接受消息的方法。例如,在全雙工模式下,可以調(diào)用conn1.send發(fā)送消息,conn1.recv接
收消息。如果沒(méi)有消息可接收,recv方法會(huì)一直阻塞。如果管道已經(jīng)被關(guān)閉,那么recv方法會(huì)拋出EOFError。
from multiprocessing import Process, Pipe
def send(conn):
? ?conn.send("Hello World")
? ?conn.close()
if __name__ == '__main__':
? ?parent_conn, child_conn = Pipe()
? ?p = Process(target=send, args=(child_conn,))
? ?p.start()
? ?print parent_conn.recv()
'''
'''
創(chuàng)建進(jìn)程示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
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 'Process will start.'
? ?p.start()
? ?print 'Process end.'
創(chuàng)建子進(jìn)程時(shí),只需要傳入一個(gè)執(zhí)行函數(shù)和函數(shù)的參數(shù),創(chuàng)建一個(gè)Process實(shí)例,用start()方法啟動(dòng)。
注意:由于進(jìn)程之間的數(shù)據(jù)需要各自持有一份,所以創(chuàng)建進(jìn)程需要的非常大的開(kāi)銷。
'''
'''
進(jìn)程鎖示例
from multiprocessing import Process, Array, RLock
def Foo(lock,temp,i):
? ?"""
? ?將第0個(gè)數(shù)加100
? ?"""
? ?lock.acquire()
? ?temp[0] = 100+i
? ?for item in temp:
? ? ? ?print i,'----->',item
? ?lock.release()
lock = RLock()
temp = Array('i', [11, 22, 33, 44])
for i in range(20):
? ?p = Process(target=Foo,args=(lock,temp,i,))
? ?p.start()
'''
'''
進(jìn)程池示例
? ?在利用Python進(jìn)行系統(tǒng)管理的時(shí)候,特別是同時(shí)操作多個(gè)文件目錄,或者遠(yuǎn)程控制多臺(tái)主機(jī),并行操作可以節(jié)約大量的時(shí)
間。當(dāng)被操作對(duì)象數(shù)目不大時(shí),可以直接利用multiprocessing中的Process動(dòng)態(tài)成生多個(gè)進(jìn)程,十幾個(gè)還好,但如果是上百
個(gè),上千個(gè)目標(biāo),手動(dòng)的去限制進(jìn)程數(shù)量卻又太過(guò)繁瑣,此時(shí)可以發(fā)揮進(jìn)程池的功效。
? ?Pool可以提供指定數(shù)量的進(jìn)程供用戶調(diào)用,當(dāng)有新的請(qǐng)求提交到pool中時(shí),如果池還沒(méi)有滿,那么就會(huì)創(chuàng)建一個(gè)新的進(jìn)程
用來(lái)執(zhí)行該請(qǐng)求;但如果池中的進(jìn)程數(shù)已經(jīng)達(dá)到規(guī)定最大值,那么該請(qǐng)求就會(huì)等待,直到池中有進(jìn)程結(jié)束,才會(huì)創(chuàng)建新的進(jìn)程
來(lái)它。
#!/usr/bin/env python
#coding:utf-8
from multiprocessing import Pool
import os, time, random
def 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.'
join()方法可以等待子進(jìn)程結(jié)束后再繼續(xù)往下運(yùn)行,通常用于進(jìn)程間的同步。
? ?task 0,1,2,3是立刻執(zhí)行的,而task 4要等待前面某個(gè)task完成后才執(zhí)行,這是因?yàn)镻ool的默認(rèn)大小在我的電腦上是4,
因此,最多同時(shí)執(zhí)行4個(gè)進(jìn)程。
'''
'''
進(jìn)程間共享數(shù)據(jù)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue
import os, time, random
# 寫(xiě)數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def write(q):
? ?for value in ['A', 'B', 'C']:
? ? ? ?print 'Put %s to queue...' % value
? ? ? ?q.put(value)
? ? ? ?time.sleep(random.random())
# 讀數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def read(q):
? ?while True:
? ? ? ?value = q.get(True)
? ? ? ?print 'Get %s from queue.' % value
if __name__=='__main__':
? ?# 父進(jìn)程創(chuàng)建Queue,并傳給各個(gè)子進(jìn)程:
? ?q = Queue()
? ?pw = Process(target=write, args=(q,))
? ?pr = Process(target=read, args=(q,))
? ?# 啟動(dòng)子進(jìn)程pw,寫(xiě)入:
? ?pw.start()
? ?# 啟動(dòng)子進(jìn)程pr,讀取:
? ?pr.start()
? ?# 等待pw結(jié)束:
? ?pw.join()
? ?# pr進(jìn)程里是死循環(huán),無(wú)法等待其結(jié)束,只能強(qiáng)行終止:
? ?pr.terminate()
進(jìn)程間默認(rèn)無(wú)法共享數(shù)據(jù)
'''
'''
Python的線程
? ?多任務(wù)可以由多進(jìn)程完成,也可以由一個(gè)進(jìn)程內(nèi)的多線程完成。進(jìn)程是由若干線程組成的,一個(gè)進(jìn)程至少有一個(gè)線程。
? ?Python的標(biāo)準(zhǔn)庫(kù)提供了兩個(gè)模塊:thread和threading,thread是低級(jí)模塊,threading是高級(jí)模塊,對(duì)thread進(jìn)行了
封裝。絕大多數(shù)情況下,我們只需要使用threading這個(gè)高級(jí)模塊。啟動(dòng)一個(gè)線程就是把一個(gè)函數(shù)傳入并創(chuàng)建Thread實(shí)例,然
后調(diào)用start()開(kāi)始執(zhí)行
'''
'''
python的多線程模塊:threading
? ?Thread ? ? ? ? ? ? ? ? ?#線程執(zhí)行的對(duì)象
? ? ? ?start ? ? ? ? ? ? ? 線程準(zhǔn)備就緒,等待CPU調(diào)度
? ? ? ?setName ? ? ? ? ? ? 為線程設(shè)置名稱
? ? ? ?getName ? ? ? ? ? ? 獲取線程名稱
? ? ? ?setDaemon ? ? ? ? ? 設(shè)置為后臺(tái)線程或前臺(tái)線程(默認(rèn))
? ? ? ? ? ? ? ? ? ? ? ? ? ?如果是后臺(tái)線程,主線程執(zhí)行過(guò)程中,后臺(tái)線程也在進(jìn)行,主線程執(zhí)行完畢后,后臺(tái)線程不
? ? ? ? ? ? ? ? ? ? ? ? ? ?論成功與否,均停止如果是前臺(tái)線程,主線程執(zhí)行過(guò)程中,前臺(tái)線程也在進(jìn)行,主線程執(zhí)行
? ? ? ? ? ? ? ? ? ? ? ? ? ?完畢后,等待前臺(tái)線程也執(zhí)行完成后,程序停止
? ? ? ?join ? ? ? ? ? ? ? ?逐個(gè)執(zhí)行每個(gè)線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行,該方法使得多線程變得無(wú)意義
? ? ? ?run ? ? ? ? ? ? ? ? 線程被cpu調(diào)度后執(zhí)行Thread類對(duì)象的run方法
? ?Rlock ? ? ? ? ? ? ? ? ? #線程鎖:可重入鎖對(duì)象.使單線程可以在此獲得已獲得了的鎖(遞歸鎖定)
? ? ? ?acquire ? ? ? ? ? ? 為線程加鎖
? ? ? ?release ? ? ? ? ? ? 為線程解鎖
? ?Event ? ? ? ? ? ? ? ? ? #python線程的事件用于主線程控制其他線程的執(zhí)行。
? ? ? ?set ? ? ? ? ? ? ? ? 將全局變量設(shè)置為T(mén)rue
? ? ? ?wait ? ? ? ? ? ? ? ?事件處理的機(jī)制:全局定義了一個(gè)“Flag”,如果“Flag”值為 False,那么當(dāng)程序執(zhí)行
? ? ? ? ? ? ? ? ? ? ? ? ? ?event.wait方法時(shí)就會(huì)阻塞,如果“Flag”值為T(mén)rue,那么event.wait 方法時(shí)便不再阻塞
? ? ? ?clear ? ? ? ? ? ? ? 將全局變量設(shè)置為False
? ?Semaphore ? ? ? ? ? ? ? 為等待鎖的線程提供一個(gè)類似等候室的結(jié)構(gòu)
? ?BoundedSemaphore ? ? ? ?與Semaphore類似,只是不允許超過(guò)初始值
? ?Time ? ? ? ? ? ? ? ? ? ?與Thread相似,只是他要等待一段時(shí)間后才開(kāi)始運(yùn)行
? ?activeCount() ? ? ? ? ? 當(dāng)前活動(dòng)的線程對(duì)象的數(shù)量
? ?currentThread() ? ? ? ? 返回當(dāng)前線程對(duì)象
? ?enumerate() ? ? ? ? ? ? 返回當(dāng)前活動(dòng)線程的列表
? ?settrace(func) ? ? ? ? ?為所有線程設(shè)置一個(gè)跟蹤函數(shù)
? ?setprofile(func) ? ? ? ?為所有線程設(shè)置一個(gè)profile函數(shù)
'''
'''
線程示例
#!/usr/bin/env python
#coding:utf-8
import threading
import time
def show(arg):
? ?time.sleep(1)
? ?print 'thread'+str(arg)
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
'''
'''
線程鎖示例
? ?多線程和多進(jìn)程最大的不同在于,多進(jìn)程中,同一個(gè)變量,各自有一份拷貝存在于每個(gè)進(jìn)程中,互不影響,而多線程中,
所有變量都由所有線程共享,所以,任何一個(gè)變量都可以被任何一個(gè)線程修改,因此,線程之間共享數(shù)據(jù)最大的危險(xiǎn)在于多個(gè)
線程同時(shí)改一個(gè)變量,把內(nèi)容給改亂了。
#!/usr/bin/env python
#coding:utf-8
import threading
import time
gl_num = 0
def show(arg):
? ?global gl_num
? ?time.sleep(1)
? ?gl_num +=1
? ?print gl_num
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
由于線程之間是進(jìn)行隨機(jī)調(diào)度,并且每個(gè)線程可能只執(zhí)行n條執(zhí)行之后,CPU接著執(zhí)行其他線程
如果按上例的話會(huì)出現(xiàn)一種情況多個(gè)線程同時(shí)修改一份內(nèi)存資源,造成數(shù)據(jù)的修改混亂那么線程鎖可以解決這個(gè)問(wèn)題
#!/usr/bin/env python
#coding:utf-8import threading
import time
gl_num = 0
lock=threading.RLock()
def show(arg):
? ?lock.acquire()
? ?global gl_num
? ?time.sleep(1)
? ?gl_num +=1
? ?print gl_num
? ?lock.release()
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
? ?因?yàn)镻ython的線程雖然是真正的線程,但解釋器執(zhí)行代碼時(shí),有一個(gè)GIL鎖:Global Interpreter Lock,任何Python
線程執(zhí)行前,必須先獲得GIL鎖,然后,每執(zhí)行100條字節(jié)碼,解釋器就自動(dòng)釋放GIL鎖,讓別的線程有機(jī)會(huì)執(zhí)行。這個(gè)GIL全
局鎖實(shí)際上把所有線程的執(zhí)行代碼都給上了鎖,所以,多線程在Python中只能交替執(zhí)行,即使100個(gè)線程跑在100核CPU上,也
只能用到1個(gè)核。
? ?GIL是Python解釋器設(shè)計(jì)的歷史遺留問(wèn)題,通常我們用的解釋器是官方實(shí)現(xiàn)的CPython,要真正利用多核,除非重寫(xiě)一個(gè)
不帶GIL的解釋器。所以,在Python中,可以使用多線程,但不要指望能有效利用多核。如果一定要通過(guò)多線程利用多核,那
只能通過(guò)C擴(kuò)展來(lái)實(shí)現(xiàn),不過(guò)這樣就失去了Python簡(jiǎn)單易用的特點(diǎn)。
? ?不過(guò),也不用過(guò)于擔(dān)心,Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù),但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)。多個(gè)Python進(jìn)程有
各自獨(dú)立的GIL鎖,互不影響。
? ?多線程編程,模型復(fù)雜,容易發(fā)生沖突,必須用鎖加以隔離,同時(shí),又要小心死鎖的發(fā)生。
? ?Python解釋器由于設(shè)計(jì)時(shí)有GIL全局鎖,導(dǎo)致了多線程無(wú)法利用多核。多線程的并發(fā)在Python中就是一個(gè)美麗的夢(mèng)。
'''
'''
線程的事件示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
def do(event):
? ?print 'start'
? ?event.wait()
? ?print 'execute'
event_obj = threading.Event()
for i in range(10):
? ?t = threading.Thread(target=do, args=(event_obj,))
? ?t.start()
event_obj.clear()
inp = raw_input('input:')
if inp == 'true':
? ?event_obj.set()
'''
'''
協(xié)程簡(jiǎn)介
? ?線程和進(jìn)程的操作是由程序觸發(fā)系統(tǒng)接口,最后的執(zhí)行者是系統(tǒng);協(xié)程的操作則是程序員。
? ?協(xié)程存在的意義:對(duì)于多線程應(yīng)用,CPU通過(guò)切片的方式來(lái)切換線程間的執(zhí)行,線程切換時(shí)需要耗時(shí)(保存狀態(tài),下次繼
續(xù))。協(xié)程,則只使用一個(gè)線程,在一個(gè)線程中規(guī)定某個(gè)代碼塊執(zhí)行順序。
? ?協(xié)程的適用場(chǎng)景:當(dāng)程序中存在大量不需要CPU的操作時(shí)(IO),適用于協(xié)程;
'''
'''
協(xié)程示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet
def test1():
? ?print 12
? ?gr2.switch()
? ?print 34
? ?gr2.switch()
def test2():
? ?print 56
? ?gr1.switch()
? ?print 78
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
'''
'''
進(jìn)程vs線程
? ?我們可以把任務(wù)分為計(jì)算密集型和IO密集型。
? ?計(jì)算密集型任務(wù)的特點(diǎn)是要進(jìn)行大量的計(jì)算,消耗CPU資源,比如計(jì)算圓周率、對(duì)視頻進(jìn)行高清解碼等等,全靠CPU的運(yùn)算
能力。這種計(jì)算密集型任務(wù)雖然也可以用多任務(wù)完成,但是任務(wù)越多,花在任務(wù)切換的時(shí)間就越多,CPU執(zhí)行任務(wù)的效率就越
低,所以,要最高效地利用CPU,計(jì)算密集型任務(wù)同時(shí)進(jìn)行的數(shù)量應(yīng)當(dāng)?shù)扔贑PU的核心數(shù)。
? ?計(jì)算密集型任務(wù)由于主要消耗CPU資源,因此,代碼運(yùn)行效率至關(guān)重要。用Python的話適合多進(jìn)程
第二種任務(wù)的類型是IO密集型,涉及到網(wǎng)絡(luò)、磁盤(pán)IO的任務(wù)都是IO密集型任務(wù),這類任務(wù)的特點(diǎn)是CPU消耗很少,任務(wù)的大部
分時(shí)間都在等待IO操作完成(因?yàn)镮O的速度遠(yuǎn)遠(yuǎn)低于CPU和內(nèi)存的速度)。對(duì)于IO密集型任務(wù),任務(wù)越多,CPU效率越高,但也
有一個(gè)限度。常見(jiàn)的大部分任務(wù)都是IO密集型任務(wù),比如Web應(yīng)用。這時(shí)候不需要cpu做過(guò)多的計(jì)算,應(yīng)當(dāng)用多線程。
'''
'''
IO多路復(fù)用
? ?I/O多路復(fù)用指:通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫(xiě)就緒),能夠通知程
序進(jìn)行相應(yīng)的讀寫(xiě)操作。目前支持I/O多路復(fù)用的系統(tǒng)調(diào)用有 select,poll,epoll
應(yīng)用場(chǎng)景:
? ?服務(wù)器需要同時(shí)處理多個(gè)處于監(jiān)聽(tīng)狀態(tài)或者多個(gè)連接狀態(tài)的套接字。
? ?服務(wù)器需要同時(shí)處理多種網(wǎng)絡(luò)協(xié)議的套接字。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import select
import socket
import Queue
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , 1)
server_address= ('192.168.1.5',8080)
server.bind(server_address)
server.listen(10)
#select輪詢等待讀socket集合
inputs = [server]
#select輪詢等待寫(xiě)socket集合
outputs = []
message_queues = {}
#select超時(shí)時(shí)間
timeout = 20
while True:
? ?print "等待活動(dòng)連接......"
? ?readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)
? ?if not (readable or writable or exceptional) :
? ? ? ?print "select超時(shí)無(wú)活動(dòng)連接,重新select...... "
? ? ? ?continue;
? ?#循環(huán)可讀事件
? ?for s in readable :
? ? ? ?#如果是server監(jiān)聽(tīng)的socket
? ? ? ?if s is server:
? ? ? ? ? ?#同意連接
? ? ? ? ? ?connection, client_address = s.accept()
? ? ? ? ? ?print "新連接: ", client_address
? ? ? ? ? ?connection.setblocking(0)
? ? ? ? ? ?#將連接加入到select可讀事件隊(duì)列
? ? ? ? ? ?inputs.append(connection)
? ? ? ? ? ?#新建連接為key的字典,寫(xiě)回讀取到的消息
? ? ? ? ? ?message_queues[connection] = Queue.Queue()
? ? ? ?else:
? ? ? ? ? ?#不是本機(jī)監(jiān)聽(tīng)就是客戶端發(fā)來(lái)的消息
? ? ? ? ? ?data = s.recv(1024)
? ? ? ? ? ?if data :
? ? ? ? ? ? ? ?print "收到數(shù)據(jù):" , data , "客戶端:",s.getpeername()
? ? ? ? ? ? ? ?message_queues[s].put(data)
? ? ? ? ? ? ? ?if s not in outputs:
? ? ? ? ? ? ? ? ? ?#將讀取到的socket加入到可寫(xiě)事件隊(duì)列
? ? ? ? ? ? ? ? ? ?outputs.append(s)
? ? ? ? ? ?else:
? ? ? ? ? ? ? ?#空白消息,關(guān)閉連接
? ? ? ? ? ? ? ?print "關(guān)閉連接:", client_address
? ? ? ? ? ? ? ?if s in outputs :
? ? ? ? ? ? ? ? ? ?outputs.remove(s)
? ? ? ? ? ? ? ?inputs.remove(s)
? ? ? ? ? ? ? ?s.close()
? ? ? ? ? ? ? ?del message_queues[s]
? ?for s in writable:
? ? ? ?try:
? ? ? ? ? ?msg = message_queues[s].get_nowait()
? ? ? ?except Queue.Empty:
? ? ? ? ? ?print "連接:" , s.getpeername() , '消息隊(duì)列為空'
? ? ? ? ? ?outputs.remove(s)
? ? ? ?else:
? ? ? ? ? ?print "發(fā)送數(shù)據(jù):" , msg , "到", s.getpeername()
? ? ? ? ? ?s.send(msg)
? ?for s in exceptional:
? ? ? ?print "異常連接:", s.getpeername()
? ? ? ?inputs.remove(s)
? ? ? ?if s in outputs:
? ? ? ? ? ?outputs.remove(s)
? ? ? ?s.close()
? ? ? ?del message_queues[s]
'''
'''
? ?進(jìn)程和線程的區(qū)別和關(guān)系:
? ?對(duì)于操作系統(tǒng)來(lái)說(shuō),一個(gè)任務(wù)就是一個(gè)進(jìn)程(Process),比如打開(kāi)一個(gè)瀏覽器就是啟動(dòng)一個(gè)瀏覽器進(jìn)程,打開(kāi)一個(gè)記事
本就啟動(dòng)了一個(gè)記事本進(jìn)程,打開(kāi)兩個(gè)記事本就啟動(dòng)了兩個(gè)記事本進(jìn)程,打開(kāi)一個(gè)Word就啟動(dòng)了一個(gè)Word進(jìn)程。
? ?有些進(jìn)程還不止同時(shí)干一件事,比如Word,它可以同時(shí)進(jìn)行打字、拼寫(xiě)檢查、打印等事情。在一個(gè)進(jìn)程內(nèi)部,要同時(shí)干多
件事,就需要同時(shí)運(yùn)行多個(gè)“子任務(wù)”,我們把進(jìn)程內(nèi)的這些“子任務(wù)”稱為線程(Thread)。
? ?由于每個(gè)進(jìn)程至少要干一件事,所以,一個(gè)進(jìn)程至少有一個(gè)線程。當(dāng)然,像Word這種復(fù)雜的進(jìn)程可以有多個(gè)線程,多個(gè)線
程可以同時(shí)執(zhí)行,多線程的執(zhí)行方式和多進(jìn)程是一樣的,也是由操作系統(tǒng)在多個(gè)線程之間快速切換,讓每個(gè)線程都短暫地交替
運(yùn)行,看起來(lái)就像同時(shí)執(zhí)行一樣。當(dāng)然,真正地同時(shí)執(zhí)行多線程需要多核CPU才可能實(shí)現(xiàn)。
? ?線程是最小的執(zhí)行單元,而進(jìn)程由至少一個(gè)線程組成。如何調(diào)度進(jìn)程和線程,完全由操作系統(tǒng)決定,程序自己不能決定什
么時(shí)候執(zhí)行,執(zhí)行多長(zhǎng)時(shí)間。
'''
'''
python的進(jìn)程
? ?multiprocessing包的組件Process, Queue, Pipe, Lock等組件提供了與多線程類似的功能。使用這些組件,可以方便
地編寫(xiě)多進(jìn)程并發(fā)程序。
'''
'''
Queue隊(duì)列
? ?Queue是多進(jìn)程安全的隊(duì)列,可以使用Queue實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)傳遞。put方法用以插入數(shù)據(jù)到隊(duì)列中,put方法還有
兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue(默認(rèn)值),并且timeout為正值,該方法會(huì)阻塞timeout指定的時(shí)
間,直到該隊(duì)列有剩余的空間。如果超時(shí),會(huì)拋出Queue.Full異常。如果blocked為False,但該Queue已滿,會(huì)立即拋出
Queue.Full異常。
? ?get方法可以從隊(duì)列讀取并且刪除一個(gè)元素。同樣,get方法有兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue
(默認(rèn)值),并且timeout為正值,那么在等待時(shí)間內(nèi)沒(méi)有取到任何元素,會(huì)拋出Queue.Empty異常。如果blocked為False,
有兩種情況存在,如果Queue有一個(gè)值可用,則立即返回該值,否則,如果隊(duì)列為空,則立即拋出Queue.Empty異常。
from multiprocessing import Process, Queue
def offer(queue):
? ?queue.put("Hello World")
if __name__ == '__main__':
? ?q = Queue()
? ?p = Process(target=offer, args=(q,))
? ?p.start()
? ?print q.get()
'''
'''
Pipes管道
? ?Pipe方法返回(conn1, conn2)代表一個(gè)管道的兩個(gè)端。Pipe方法有duplex參數(shù),如果duplex參數(shù)為T(mén)rue(默認(rèn)值)那么
這個(gè)管道是全雙工模式,也就是說(shuō)conn1和conn2均可收發(fā)。duplex為False,conn1只負(fù)責(zé)接受消息,conn2只負(fù)責(zé)發(fā)送消息
? ?send和recv方法分別是發(fā)送和接受消息的方法。例如,在全雙工模式下,可以調(diào)用conn1.send發(fā)送消息,conn1.recv接
收消息。如果沒(méi)有消息可接收,recv方法會(huì)一直阻塞。如果管道已經(jīng)被關(guān)閉,那么recv方法會(huì)拋出EOFError。
from multiprocessing import Process, Pipe
def send(conn):
? ?conn.send("Hello World")
? ?conn.close()
if __name__ == '__main__':
? ?parent_conn, child_conn = Pipe()
? ?p = Process(target=send, args=(child_conn,))
? ?p.start()
? ?print parent_conn.recv()
'''
'''
創(chuàng)建進(jìn)程示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
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 'Process will start.'
? ?p.start()
? ?print 'Process end.'
創(chuàng)建子進(jìn)程時(shí),只需要傳入一個(gè)執(zhí)行函數(shù)和函數(shù)的參數(shù),創(chuàng)建一個(gè)Process實(shí)例,用start()方法啟動(dòng)。
注意:由于進(jìn)程之間的數(shù)據(jù)需要各自持有一份,所以創(chuàng)建進(jìn)程需要的非常大的開(kāi)銷。
'''
'''
進(jìn)程鎖示例
from multiprocessing import Process, Array, RLock
def Foo(lock,temp,i):
? ?"""
? ?將第0個(gè)數(shù)加100
? ?"""
? ?lock.acquire()
? ?temp[0] = 100+i
? ?for item in temp:
? ? ? ?print i,'----->',item
? ?lock.release()
lock = RLock()
temp = Array('i', [11, 22, 33, 44])
for i in range(20):
? ?p = Process(target=Foo,args=(lock,temp,i,))
? ?p.start()
'''
'''
進(jìn)程池示例
? ?在利用Python進(jìn)行系統(tǒng)管理的時(shí)候,特別是同時(shí)操作多個(gè)文件目錄,或者遠(yuǎn)程控制多臺(tái)主機(jī),并行操作可以節(jié)約大量的時(shí)
間。當(dāng)被操作對(duì)象數(shù)目不大時(shí),可以直接利用multiprocessing中的Process動(dòng)態(tài)成生多個(gè)進(jìn)程,十幾個(gè)還好,但如果是上百
個(gè),上千個(gè)目標(biāo),手動(dòng)的去限制進(jìn)程數(shù)量卻又太過(guò)繁瑣,此時(shí)可以發(fā)揮進(jìn)程池的功效。
? ?Pool可以提供指定數(shù)量的進(jìn)程供用戶調(diào)用,當(dāng)有新的請(qǐng)求提交到pool中時(shí),如果池還沒(méi)有滿,那么就會(huì)創(chuàng)建一個(gè)新的進(jìn)程
用來(lái)執(zhí)行該請(qǐng)求;但如果池中的進(jìn)程數(shù)已經(jīng)達(dá)到規(guī)定最大值,那么該請(qǐng)求就會(huì)等待,直到池中有進(jìn)程結(jié)束,才會(huì)創(chuàng)建新的進(jìn)程
來(lái)它。
#!/usr/bin/env python
#coding:utf-8
from multiprocessing import Pool
import os, time, random
def 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.'
join()方法可以等待子進(jìn)程結(jié)束后再繼續(xù)往下運(yùn)行,通常用于進(jìn)程間的同步。
? ?task 0,1,2,3是立刻執(zhí)行的,而task 4要等待前面某個(gè)task完成后才執(zhí)行,這是因?yàn)镻ool的默認(rèn)大小在我的電腦上是4,
因此,最多同時(shí)執(zhí)行4個(gè)進(jìn)程。
'''
'''
進(jìn)程間共享數(shù)據(jù)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue
import os, time, random
# 寫(xiě)數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def write(q):
? ?for value in ['A', 'B', 'C']:
? ? ? ?print 'Put %s to queue...' % value
? ? ? ?q.put(value)
? ? ? ?time.sleep(random.random())
# 讀數(shù)據(jù)進(jìn)程執(zhí)行的代碼:
def read(q):
? ?while True:
? ? ? ?value = q.get(True)
? ? ? ?print 'Get %s from queue.' % value
if __name__=='__main__':
? ?# 父進(jìn)程創(chuàng)建Queue,并傳給各個(gè)子進(jìn)程:
? ?q = Queue()
? ?pw = Process(target=write, args=(q,))
? ?pr = Process(target=read, args=(q,))
? ?# 啟動(dòng)子進(jìn)程pw,寫(xiě)入:
? ?pw.start()
? ?# 啟動(dòng)子進(jìn)程pr,讀取:
? ?pr.start()
? ?# 等待pw結(jié)束:
? ?pw.join()
? ?# pr進(jìn)程里是死循環(huán),無(wú)法等待其結(jié)束,只能強(qiáng)行終止:
? ?pr.terminate()
進(jìn)程間默認(rèn)無(wú)法共享數(shù)據(jù)
'''
'''
Python的線程
? ?多任務(wù)可以由多進(jìn)程完成,也可以由一個(gè)進(jìn)程內(nèi)的多線程完成。進(jìn)程是由若干線程組成的,一個(gè)進(jìn)程至少有一個(gè)線程。
? ?Python的標(biāo)準(zhǔn)庫(kù)提供了兩個(gè)模塊:thread和threading,thread是低級(jí)模塊,threading是高級(jí)模塊,對(duì)thread進(jìn)行了
封裝。絕大多數(shù)情況下,我們只需要使用threading這個(gè)高級(jí)模塊。啟動(dòng)一個(gè)線程就是把一個(gè)函數(shù)傳入并創(chuàng)建Thread實(shí)例,然
后調(diào)用start()開(kāi)始執(zhí)行
'''
'''
python的多線程模塊:threading
? ?Thread ? ? ? ? ? ? ? ? ?#線程執(zhí)行的對(duì)象
? ? ? ?start ? ? ? ? ? ? ? 線程準(zhǔn)備就緒,等待CPU調(diào)度
? ? ? ?setName ? ? ? ? ? ? 為線程設(shè)置名稱
? ? ? ?getName ? ? ? ? ? ? 獲取線程名稱
? ? ? ?setDaemon ? ? ? ? ? 設(shè)置為后臺(tái)線程或前臺(tái)線程(默認(rèn))
? ? ? ? ? ? ? ? ? ? ? ? ? ?如果是后臺(tái)線程,主線程執(zhí)行過(guò)程中,后臺(tái)線程也在進(jìn)行,主線程執(zhí)行完畢后,后臺(tái)線程不
? ? ? ? ? ? ? ? ? ? ? ? ? ?論成功與否,均停止如果是前臺(tái)線程,主線程執(zhí)行過(guò)程中,前臺(tái)線程也在進(jìn)行,主線程執(zhí)行
? ? ? ? ? ? ? ? ? ? ? ? ? ?完畢后,等待前臺(tái)線程也執(zhí)行完成后,程序停止
? ? ? ?join ? ? ? ? ? ? ? ?逐個(gè)執(zhí)行每個(gè)線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行,該方法使得多線程變得無(wú)意義
? ? ? ?run ? ? ? ? ? ? ? ? 線程被cpu調(diào)度后執(zhí)行Thread類對(duì)象的run方法
? ?Rlock ? ? ? ? ? ? ? ? ? #線程鎖:可重入鎖對(duì)象.使單線程可以在此獲得已獲得了的鎖(遞歸鎖定)
? ? ? ?acquire ? ? ? ? ? ? 為線程加鎖
? ? ? ?release ? ? ? ? ? ? 為線程解鎖
? ?Event ? ? ? ? ? ? ? ? ? #python線程的事件用于主線程控制其他線程的執(zhí)行。
? ? ? ?set ? ? ? ? ? ? ? ? 將全局變量設(shè)置為T(mén)rue
? ? ? ?wait ? ? ? ? ? ? ? ?事件處理的機(jī)制:全局定義了一個(gè)“Flag”,如果“Flag”值為 False,那么當(dāng)程序執(zhí)行
? ? ? ? ? ? ? ? ? ? ? ? ? ?event.wait方法時(shí)就會(huì)阻塞,如果“Flag”值為T(mén)rue,那么event.wait 方法時(shí)便不再阻塞
? ? ? ?clear ? ? ? ? ? ? ? 將全局變量設(shè)置為False
? ?Semaphore ? ? ? ? ? ? ? 為等待鎖的線程提供一個(gè)類似等候室的結(jié)構(gòu)
? ?BoundedSemaphore ? ? ? ?與Semaphore類似,只是不允許超過(guò)初始值
? ?Time ? ? ? ? ? ? ? ? ? ?與Thread相似,只是他要等待一段時(shí)間后才開(kāi)始運(yùn)行
? ?activeCount() ? ? ? ? ? 當(dāng)前活動(dòng)的線程對(duì)象的數(shù)量
? ?currentThread() ? ? ? ? 返回當(dāng)前線程對(duì)象
? ?enumerate() ? ? ? ? ? ? 返回當(dāng)前活動(dòng)線程的列表
? ?settrace(func) ? ? ? ? ?為所有線程設(shè)置一個(gè)跟蹤函數(shù)
? ?setprofile(func) ? ? ? ?為所有線程設(shè)置一個(gè)profile函數(shù)
'''
'''
線程示例
#!/usr/bin/env python
#coding:utf-8
import threading
import time
def show(arg):
? ?time.sleep(1)
? ?print 'thread'+str(arg)
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
'''
'''
線程鎖示例
? ?多線程和多進(jìn)程最大的不同在于,多進(jìn)程中,同一個(gè)變量,各自有一份拷貝存在于每個(gè)進(jìn)程中,互不影響,而多線程中,
所有變量都由所有線程共享,所以,任何一個(gè)變量都可以被任何一個(gè)線程修改,因此,線程之間共享數(shù)據(jù)最大的危險(xiǎn)在于多個(gè)
線程同時(shí)改一個(gè)變量,把內(nèi)容給改亂了。
#!/usr/bin/env python
#coding:utf-8
import threading
import time
gl_num = 0
def show(arg):
? ?global gl_num
? ?time.sleep(1)
? ?gl_num +=1
? ?print gl_num
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
由于線程之間是進(jìn)行隨機(jī)調(diào)度,并且每個(gè)線程可能只執(zhí)行n條執(zhí)行之后,CPU接著執(zhí)行其他線程
如果按上例的話會(huì)出現(xiàn)一種情況多個(gè)線程同時(shí)修改一份內(nèi)存資源,造成數(shù)據(jù)的修改混亂那么線程鎖可以解決這個(gè)問(wèn)題
#!/usr/bin/env python
#coding:utf-8import threading
import time
gl_num = 0
lock=threading.RLock()
def show(arg):
? ?lock.acquire()
? ?global gl_num
? ?time.sleep(1)
? ?gl_num +=1
? ?print gl_num
? ?lock.release()
for i in range(10):
? ?t = threading.Thread(target=show, args=(i,))
? ?t.start()
print 'main thread stop'
? ?因?yàn)镻ython的線程雖然是真正的線程,但解釋器執(zhí)行代碼時(shí),有一個(gè)GIL鎖:Global Interpreter Lock,任何Python
線程執(zhí)行前,必須先獲得GIL鎖,然后,每執(zhí)行100條字節(jié)碼,解釋器就自動(dòng)釋放GIL鎖,讓別的線程有機(jī)會(huì)執(zhí)行。這個(gè)GIL全
局鎖實(shí)際上把所有線程的執(zhí)行代碼都給上了鎖,所以,多線程在Python中只能交替執(zhí)行,即使100個(gè)線程跑在100核CPU上,也
只能用到1個(gè)核。
? ?GIL是Python解釋器設(shè)計(jì)的歷史遺留問(wèn)題,通常我們用的解釋器是官方實(shí)現(xiàn)的CPython,要真正利用多核,除非重寫(xiě)一個(gè)
不帶GIL的解釋器。所以,在Python中,可以使用多線程,但不要指望能有效利用多核。如果一定要通過(guò)多線程利用多核,那
只能通過(guò)C擴(kuò)展來(lái)實(shí)現(xiàn),不過(guò)這樣就失去了Python簡(jiǎn)單易用的特點(diǎn)。
? ?不過(guò),也不用過(guò)于擔(dān)心,Python雖然不能利用多線程實(shí)現(xiàn)多核任務(wù),但可以通過(guò)多進(jìn)程實(shí)現(xiàn)多核任務(wù)。多個(gè)Python進(jìn)程有
各自獨(dú)立的GIL鎖,互不影響。
? ?多線程編程,模型復(fù)雜,容易發(fā)生沖突,必須用鎖加以隔離,同時(shí),又要小心死鎖的發(fā)生。
? ?Python解釋器由于設(shè)計(jì)時(shí)有GIL全局鎖,導(dǎo)致了多線程無(wú)法利用多核。多線程的并發(fā)在Python中就是一個(gè)美麗的夢(mèng)。
'''
'''
線程的事件示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
def do(event):
? ?print 'start'
? ?event.wait()
? ?print 'execute'
event_obj = threading.Event()
for i in range(10):
? ?t = threading.Thread(target=do, args=(event_obj,))
? ?t.start()
event_obj.clear()
inp = raw_input('input:')
if inp == 'true':
? ?event_obj.set()
'''
'''
協(xié)程簡(jiǎn)介
? ?線程和進(jìn)程的操作是由程序觸發(fā)系統(tǒng)接口,最后的執(zhí)行者是系統(tǒng);協(xié)程的操作則是程序員。
? ?協(xié)程存在的意義:對(duì)于多線程應(yīng)用,CPU通過(guò)切片的方式來(lái)切換線程間的執(zhí)行,線程切換時(shí)需要耗時(shí)(保存狀態(tài),下次繼
續(xù))。協(xié)程,則只使用一個(gè)線程,在一個(gè)線程中規(guī)定某個(gè)代碼塊執(zhí)行順序。
? ?協(xié)程的適用場(chǎng)景:當(dāng)程序中存在大量不需要CPU的操作時(shí)(IO),適用于協(xié)程;
'''
'''
協(xié)程示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet
def test1():
? ?print 12
? ?gr2.switch()
? ?print 34
? ?gr2.switch()
def test2():
? ?print 56
? ?gr1.switch()
? ?print 78
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
'''
'''
進(jìn)程vs線程
? ?我們可以把任務(wù)分為計(jì)算密集型和IO密集型。
? ?計(jì)算密集型任務(wù)的特點(diǎn)是要進(jìn)行大量的計(jì)算,消耗CPU資源,比如計(jì)算圓周率、對(duì)視頻進(jìn)行高清解碼等等,全靠CPU的運(yùn)算
能力。這種計(jì)算密集型任務(wù)雖然也可以用多任務(wù)完成,但是任務(wù)越多,花在任務(wù)切換的時(shí)間就越多,CPU執(zhí)行任務(wù)的效率就越
低,所以,要最高效地利用CPU,計(jì)算密集型任務(wù)同時(shí)進(jìn)行的數(shù)量應(yīng)當(dāng)?shù)扔贑PU的核心數(shù)。
? ?計(jì)算密集型任務(wù)由于主要消耗CPU資源,因此,代碼運(yùn)行效率至關(guān)重要。用Python的話適合多進(jìn)程
第二種任務(wù)的類型是IO密集型,涉及到網(wǎng)絡(luò)、磁盤(pán)IO的任務(wù)都是IO密集型任務(wù),這類任務(wù)的特點(diǎn)是CPU消耗很少,任務(wù)的大部
分時(shí)間都在等待IO操作完成(因?yàn)镮O的速度遠(yuǎn)遠(yuǎn)低于CPU和內(nèi)存的速度)。對(duì)于IO密集型任務(wù),任務(wù)越多,CPU效率越高,但也
有一個(gè)限度。常見(jiàn)的大部分任務(wù)都是IO密集型任務(wù),比如Web應(yīng)用。這時(shí)候不需要cpu做過(guò)多的計(jì)算,應(yīng)當(dāng)用多線程。
'''
轉(zhuǎn)載于:https://blog.51cto.com/lxb994/1728238
總結(jié)
以上是生活随笔為你收集整理的python线程与进程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 回文树笔记(转自quack_quack)
- 下一篇: Python 第十一章 常用第三方模块