在操做系統中,每個獨立運行的程序,都佔有 操做系統 分配的資源,這些程序中間互不干涉,都只負責運行本身的程序代碼,這就是進程。python
可是當操做系統頻繁的建立銷燬進程時,大量的系統資源被浪費在建立和銷燬的過程當中。而隨着多核心cpu
的出現,線程也逐漸代替了進程,成爲了操做系統 能夠獨立運行的基本單位。shell
當進程不是多線程程序時,存在於進程當中的惟一線程,即是進程自己運行的代碼塊。網絡
而當多線程出如今一個進程中時,則多個線程之間共享此進程的資源,並接受操做系統的調度來運行每一個線程。多線程
爲了瞭解協程的概念,咱們先來了解一下併發和並行。併發
並行比較好理解,即有多個程序在同時執行,這裏的程序指的是操做系統的線程。app
每一個 cpu
核心,只能在同一個時刻運行一組指令,意味着同一時刻,一個核心上只有一個線程在執行。異步
當 cpu
有四個核心時,他只能夠執行4個線程。async
想要了解併發,就須要知道和
。函數
當程序中的一個 I/O
操做,會佔據比較長的時間,這時候,程序沒有被掛起,且一直在等待網絡數據傳輸,沒法進行其餘操做,這時候就是同步阻塞。spa
同步的一個概念就是,網絡傳輸完成後也沒法告知主程序操做完成,這就致使了主程序:
固然,輪詢時候能夠進行其餘的操做,這時候,就是非阻塞的狀態,即 同步非阻塞。
非阻塞的概念即主程序能夠進行其餘的操做。
有同步,就有異步。
而異步阻塞與同步阻塞相同,主程序啥也不幹,就等着 I/O 操做完成。
異步非阻塞狀態,即是併發的關鍵。
當主程序使用異步 I/O 操做時,並不會影響主程序後續的運行,而當異步 I/O 操做完成後,會主動通知主程序進行其餘操做,這樣就減小了輪詢過程當中的資源消耗,專一於其餘工做。
而併發就是 異步非阻塞
狀態下的一種形式,當程序執行操做 a 時,使 a 的 I/O 異步操做,這時程序去執行操做 b, 在外部看來,a 和 b 時同時被執行的,然而他們只運行在在一個線程當中。
與線程、進程不一樣的是,協程並非操做系統物理層面存在的一種程序。
協程是程序級別的,由程序編寫者本身操控整個協程的生命週期。這樣就實現了相似操做系統操做多線程同樣的效果,可是省下了現成的切換中形成的資源消耗。
而經過程序來操縱協程,就形成了cpu 一直在運行,而且是多個協程一直在運行的假象,也就變成了併發。
下面咱們經過幾個實例來講明,在 python 中的進程、線程和協程的關係。
在 python
中咱們如何編寫多進程的程序呢?
答案是使用模塊 multiprocessing
進行實現。
import time
from multiprocessing import Process
class Test(Process):
def __init__(self):
super().__init__()
def run(self):
while True:
print("process b is run")
time.sleep(1)
複製代碼
經過繼承multiprocessing
的 Process
,實現進程類,而後實現 run
方法,便可在此方法中實現進程要運行的內容。
from process_b import Test
import time
if __name__ == "__main__":
t = Test()
t.start()
while True:
print("process a run")
time.sleep(1)
複製代碼
調用方法也很是簡單,直接使用 Test
實例化對象,而後調用對象的成員函數start()
便可。
python3 process_a.py
process a run
process b is run
process b is run
process a run
process a run
process b is run
process b is run
process a run
複製代碼
Cpython
中因爲存在 GIL
,因此多線程在實際應用中也會變爲單核cpu
上的線程,排隊運行。
import threading
import time
class ThreadTest (threading.Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
while True:
print(f"i am in thread {self.name}")
time.sleep(1)
if __name__ == "__main__":
threads = []
for i in range(4):
t = ThreadTest(i)
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
複製代碼
經過繼承 threading.Thread
來實現線程類,而後經過實例化,生成對象,調用對象的 start()
便可啓動線程。
運行結果
python3 thread_a.py
i am in thread 0
i am in thread 1
i am in thread 2
i am in thread 3
i am in thread 1
i am in thread 3
i am in thread 0
i am in thread 2
i am in thread 1
i am in thread 3
i am in thread 0
i am in thread 2
i am in thread 1
複製代碼
python3
將 asyncio
加入到了標準庫。
import asyncio
import time
async def test(num):
await asyncio.sleep(num)
print(num)
async def run():
tasks = [asyncio.create_task(test(num)) for num in range(4)]
[await t for t in tasks]
def run_main():
asyncio.run(run())
if __name__ == "__main__":
run_main()
複製代碼
運行結果
import asyncio
import time
async def test(num):
await asyncio.sleep(num)
print(num)
async def run():
tasks = [asyncio.create_task(test(num)) for num in range(4)]
[await t for t in tasks]
def run_main():
asyncio.run(run())
if __name__ == "__main__":
run_main()
複製代碼
以上就是本節的全部內容,主要簡單地講解了關於 進程、線程和協程
的概念和例子。