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做過多的計算,應當用多線程。

'''


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章