進程、線程和協程之間的關系和區別也困擾我一陣子了,最近有一些心得,寫一下。
進程擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,進程由操作系統調度。
線程擁有自己獨立的棧和共享的堆,共享堆,不共享棧,線程亦由操作系統調度(標準線程是的)。
協程和線程一樣共享堆,不共享棧,協程由程序員在協程的代碼里顯示調度。
進程和其他兩個的區別還是很明顯的。
協程和線程的區別是:協程避免了無意義的調度,由此可以提高性能,但也因此,程序員必須自己承擔調度的責任,同時,協程也失去了標準線程使用多CPU的能力。
Python線程
定義:Threading用于提供線程相關的操作,線程是應用程序中工作的最小單元。
#!/usr/bin/env python# -*- coding:utf-8 -*-import threadingimport timedef 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
上述代碼創建了10個“前臺”線程,然后控制器就交給了CPU,CPU根據指定算法進行調度,分片執行指令。
更多方法:
•start 線程準備就緒,等待CPU調度
•setName 為線程設置名稱
•getName 獲取線程名稱
•setDaemon 設置為后臺線程或前臺線程(默認)
如果是后臺線程,主線程執行過程中,后臺線程也在進行,主線程執行完畢后,后臺線程不論成功與否,均停止
如果是前臺線程,主線程執行過程中,前臺線程也在進行,主線程執行完畢后,等待前臺線程也執行完成后,程序停止
•join 逐個執行每個線程,執行完畢后繼續往下執行,該方法使得多線程變得無意義
•run 線程被cpu調度后自動執行線程對象的run方法
線程鎖
由于線程之間是進行隨機調度,并且每個線程可能只執行n條執行之后,CPU接著執行其他線程。所以,可能出現如下問題:
import threadingimport timegl_num = 0def show(arg):global gl_numtime.sleep(1)gl_num +=1print gl_numfor i in range(10):t = threading.Thread(target=show, args=(i,))t.start()print 'main thread stop' import threadingimport timegl_num = 0lock = threading.RLock()def Func():lock.acquire()global gl_numgl_num +=1time.sleep(1)print gl_numlock.release()for i in range(10):t = threading.Thread(target=Func)t.start()
event
python線程的事件用于主線程控制其他線程的執行,事件主要提供了三個方法 set、wait、clear。
事件處理的機制:全局定義了一個“Flag”,如果“Flag”值為 False,那么當程序執行 event.wait 方法時就會阻塞,如果“Flag”值為True,那么event.wait 方法時便不再阻塞。
•clear:將“Flag”設置為False
•set:將“Flag”設置為True
#!/usr/bin/env python# -*- coding:utf-8 -*-import threadingdef 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()
Python 進程
from multiprocessing import Processimport threadingimport timedef foo(i):print 'say hi',ifor i in range(10):p = Process(target=foo,args=(i,))p.start()
注意:由于進程之間的數據需要各自持有一份,所以創建進程需要的非常大的開銷。
進程數據共享
進程各自持有一份數據,默認無法共享數據
#!/usr/bin/env python#coding:utf-8from multiprocessing import Processfrom multiprocessing import Managerimport timeli = []def foo(i):li.append(i)print 'say hi',lifor i in range(10):p = Process(target=foo,args=(i,))p.start()print ('ending',li)
#方法一,Array
from multiprocessing import Process,Arraytemp = Array('i', [11,22,33,44])def Foo(i):temp[i] = 100+ifor item in temp:print i,'----->',itemfor i in range(2):p = Process(target=Foo,args=(i,))p.start()
#方法二:manage.dict()共享數據
from multiprocessing import Process,Managermanage = Manager()dic = manage.dict()def Foo(i):dic[i] = 100+iprint dic.values()for i in range(2):p = Process(target=Foo,args=(i,))p.start()p.join() 'c': ctypes.c_char, 'u': ctypes.c_wchar,'b': ctypes.c_byte, 'B': ctypes.c_ubyte,'h': ctypes.c_short, 'H': ctypes.c_ushort,'i': ctypes.c_int, 'I': ctypes.c_uint,'l': ctypes.c_long, 'L': ctypes.c_ulong,'f': ctypes.c_float, 'd': ctypes.c_double
當創建進程時(非使用時),共享數據會被拿到子進程中,當進程中執行完畢后,再賦值給原值。
#!/usr/bin/env python# -*- coding:utf-8 -*-from multiprocessing import Process, Array, RLockdef Foo(lock,temp,i):"""將第0個數加100"""lock.acquire()temp[0] = 100+ifor item in temp:print i,'----->',itemlock.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()
進程池
進程池內部維護一個進程序列,當使用時,則去進程池中獲取一個進程,如果進程池序列中沒有可供使用的進進程,那么程序就會等待,直到進程池中有可用進程為止。
進程池中有兩個方法:
•apply
•apply_async
#!/usr/bin/env python# -*- coding:utf-8 -*-from multiprocessing import Process,Poolimport timedef Foo(i):time.sleep(2)return i+100def Bar(arg):print argpool = Pool(5)#print pool.apply(Foo,(1,))#print pool.apply_async(func =Foo, args=(1,)).get()for i in range(10):pool.apply_async(func=Foo, args=(i,),callback=Bar)print 'end'pool.close()
pool.join()#進程池中進程執行完畢后再關閉,如果注釋,那么程序直接關閉
協程
線程和進程的操作是由程序觸發系統接口,最后的執行者是系統;協程的操作則是程序員。
協程存在的意義:對于多線程應用,CPU通過切片的方式來切換線程間的執行,線程切換時需要耗時(保存狀態,下次繼續)。協程,則只使用一個線程,在一個線程中規定某個代碼塊執行順序。
協程的適用場景:當程序中存在大量不需要CPU的操作時(IO),適用于協程;
greenlet
#!/usr/bin/env python# -*- coding:utf-8 -*-from greenlet import greenletdef test1():print 12gr2.switch()print 34gr2.switch()def test2():print 56gr1.switch()print 78gr1 = greenlet(test1)gr2 = greenlet(test2)gr1.switch()
gevent
import geventdef foo():print('Running in foo')gevent.sleep(0)print('Explicit context switch to foo again')def bar():print('Explicit context to bar')gevent.sleep(0)print('Implicit context switch back to bar')gevent.joinall([gevent.spawn(foo),gevent.spawn(bar),])
遇到IO操作自動切換:
from gevent import monkey; monkey.patch_all()import geventimport urllib2def f(url):print('GET: %s' % url)resp = urllib2.urlopen(url)data = resp.read()print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(f, 'https://www.python.org/'),gevent.spawn(f, 'https://www.yahoo.com/'),gevent.spawn(f, 'https://m.vevb.com/'),])
以上所述是小編給大家介紹的Python中的進程、線程、協程的相關知識,希望對大家有所幫助!