建議用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做過多的計算,應當用多線程。
'''
python線程與進程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.