最近東西積攢了太多,感覺再不寫進來就要炸了。 1.多線程 1.11 關於多線程的包 相關的python包有幾個,比如thread包,到py3改成_thread,而thread有一些問題使得不是很好用。通用的包叫threading。最近都是在用這個。 1.12 threading的使用和常用屬性 需要 ...
最近東西積攢了太多,感覺再不寫進來就要炸了。
1.多線程
1.11 關於多線程的包
相關的python包有幾個,比如thread包,到py3改成_thread,而thread有一些問題使得不是很好用。通用的包叫threading。最近都是在用這個。
1.12 threading的使用和常用屬性
需要註意的點有生成實例比如t = threading.Thread(target=xxx, args=(xx,)),裡面有兩個參數,第一個是目標函數,第二個是相關的參數,註意類型。
然後就是start啟動,join等待多線程實行完成這兩個方法。
#偽代碼 t1 = threading.Thread(target=fun1, args=("函數參數 1",)) t1.start() t2 = threading.Thread(target=fun2, args=("函數參數1", "函數參數2")) t2.start() #等待線程 t1.join() t2.join()
繼承threading.Thread使用,需要重寫run函數。
class MyThread(threading.Thread):
def __init__(self, arg):
super(MyThread, self).__init__()
self.arg = arg
def run(self):
time.sleep(2)
其他常用屬性和方法有:
threading.currentThread:返回當前線程變數
threading.enumerate:返回一個包含正在運行的線程的list,正在運行的線程指的是線程啟動後,結束前的狀態
threading.activeCount: 返回正在運行的線程數量,效果跟 len(threading.enumerate)相同
threading.timer: 定時器,利用多線程在指定時間後啟動一個功能
thr.setName: 給線程設置名字
thr.getName: 得到線程的名字
守護線程:daemon:即如果將子線程設置成守護線程,則子線程會在主線程結束的時候自動退出,一般認為,守護線程不中要或者不允許離開主線程獨立運行
而守護線程能否有效果跟環境相關
t1 = threading.Thread(target=fun, args=() ) # 社會守護線程的方法,必須在start之前設置,否則無效 t1.setDaemon(True) #t1.daemon = True t1.start()
1.13 線程相關的問題
因為線程之間有共用狀態(資源),會有一些諸如死鎖或者同步的問題。解決方法也是大同小異,這些python里都給出了
相應的工具。比如自己設置一個鎖/信號燈。用semphore變數,還有可重入鎖解鎖遞歸的時候申請鎖的問題等等。
線程安全不安全變數list,set,dict,安全變數queue等。
1.14 線程替代方案
替代無非是用進程或者魔改的線程包,諸如subprocess使用進程,multiprocessing用threading派生出來的,使用子進程,可以使用多核或多CPU
還有一個current.future待會兒寫
2.多進程
使用的就是上面提到的線程替代方案multiprocessing,方法和threading差不多,multiprocessing.Process(target=xxx, args=(xx,)),然後同樣也可以繼承後使用,實現方式類似多線程
還有一個就是pid和ppid,即進程ID和父進程ID,還有其他常見的內容比如子進程和父進程共用資源啊,而子線程有自己獨立的棧空間啊之類的就不詳細描述了
還有其他的諸如生產者-消費者模型,讀者-寫者模型等等。和上面的一樣,也不贅述了,操作系統書里都有,寫在這沒什麼意義。
3.協程
首先來寫一下協程的定義:協程是為非搶占式多任務產生子程式的電腦程式組件, 協程允許不同入口點在不同位置暫停或開始執行程式。
在這之前先寫兩個
3.11 迭代器
有兩個概念,一是可迭代對象Iterable,二是迭代器Iterator
Iterable可以用於for迴圈,二Iterator不僅可以用於for迴圈還能被next函數調用,可以用isinstance函數判斷,比如list是可迭代的。
from collections import Iterable l = [1,2,3,4] isinstance(l, Iterable)
out:True
兩者之間還可以通過iter函數轉換
from collections import Iterator isinstance(iter('abc'), Iterator)
out:True
3.12 生成器
比如最常見的生成器有L = [x * x for x in range(10)],
g = (x * x for x in range(10))
這些生成列表,元組的方法
本質是一個函數/方法,每次調用next的時候計算下一個值,最後會拋出StopIteration異常。
函數中包含yield,則叫generator
next調用,遇到yield返回
# 在函數odd中,yield負責返回 def odd(): print("Step 1") yield 1 print("Step 2") yield 2 print("Step 3") yield 3 # odd() 是調用生成器 g = odd() one = next(g) print(one) two = next(g) print(two) three = next(g) print(three)
out:Step 1
1
Step 2
2
Step 3
3
3.13 協程
是的,協程和生成器很像,使用yield和send。目的是合理調用各種系統資源和進行協同工作。協程之間切換耗費也很低
協程有四個狀態:
GEN_CREATED:等待開始執行
GEN_RUNNING:解釋器正在執行
GEN_SUSPENED:在yield表達式處暫停
GEN_CLOSED:執行結束
def simple_coroutine(a): print('-> start') b = yield a # 把A丟出來給aa print('-> recived', a, b) c = yield a + b print('-> recived', a, b, c) # runc sc = simple_coroutine(5) aa = next(sc) # 預激,準備好執行協程,程式走到yield停下 print(aa) bb = sc.send(6) # 5 ,6 把參數6發給b print(bb) cc = sc.send(7) # 5, 6, 7 print(cc)#執行不到,拋出StopIteration異常
out:
-> start
5
-> recived 5 6
11
-> recived 5 6 7
之前說過,最後會拋出一個StopIteration異常,未處理的異常會向上冒泡,傳給next函數或send函數的調用方。終止協程可以發送某個哨符,讓協程退出,比如內置的None和Ellipsis等常量
還有兩個和異常相關的方法generator.throw(Exctpiton)和generator.close()
大概作用就是前一個會在暫停的yield出拋出指定的異常。如果生成器處理了該異常,則代碼會執行到下一個yield
而產出的值(value,異常的一個屬性)會成為調用 generator.throw方法得到的返回值。沒處理則往上冒泡。
第二個的作用是致使生成器在暫停的 yield 表達式處拋出 GeneratorExit 異常。如果生成器沒有處理這個異常,或者拋出了 StopIteration 異常(通常是指運行到結尾),調用方不會報錯。
如果收到 GeneratorExit 異常,生成器一定不能產出值,否則解釋器會拋出RuntimeError 異常。生成器拋出的其他異常會向上冒泡,傳給調用方。
以下是找到的一段事例代碼
class DemoException(Exception): """ custom exception """ pass def handle_exception(): print('-> start') while True: try: x = yield except DemoException: print('-> run demo exception') else: print('-> recived x:', x) raise RuntimeError('this line should never run') he = handle_exception() next(he) he.send(10) # recived x: 10 he.send(20) # recived x: 20 he.throw(DemoException) # run demo exception he.send(40) # recived x: 40 he.close()View Code
yield from
相對於yield,yield from就相當於在調用方和生成器之間多了一層“通道”類似的代理。(又或者說在主線程和協程之間)
使用的渠道:比如委派生成器。
1,定義一個生成器(含yield)可以一次次的接收調用方傳來的值(通過yield)和return回去處理後的值
2,定義個委派生成器,只需要yield from 生成器即可使用並接收值
3,調用委派生成器使用