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