并发编程
一.什么是并發編程?
并發指的是多個任務可以被同時執行.
并發編程就是找到一種方案,讓一個程序中的多個任務可以同時被處理.
二.什么是進程?
進程指的是正在運行的程序,是一系列程序的統稱,也是操作系統在調度和進行資源分配的基本單位.
進程是實現并發的一種方式
三.多進程的實現原理:多道技術
操作系統的發展歷史:
第一代計算機:真空管和穿孔卡片
特點:沒有操作系統概念,所有程序直接操作硬件
第二代計算機:晶體管和批處理系統
特點:設計人員,生產人員,操作人員,程序人員,維護人員直接有了明確的分工,計算機被鎖在專用的空調房間中,由專業的操作人員運行,這便是'大型機'.
第三代計算機:集成電洛芯片和多道程序設計
多道技術中的多道指的是多個程序,多道技術的實現是為了解決多個程序競爭或者說共享同一資源(比如cpu)的有序調度問題,解決方式即多路復用,多路復用分為時間上的復用和空間上的復用.
空間上的復用:將內存分為幾部分,每個部分放入一個程序,這樣,同一時間內存中就有了多道程序.
時間上的復用:當一個程序在等待I/O或者占用CPU時間過長時,操作系統就可以控制進程的切換,這樣另一個程序就可以使用CPU,如果內存中可以同時存放足夠多的作業,則cpu的利用率可以接近100%.
空間上的復用最大問題:程序之間的內存必須分割,這種分割需要在硬件層面實現,由操作系統控制.
多道技術案例:生活中我們進程會同時做多個任務,但是本質上一個人是不可能同時執行多個任務.
例:吃法和打游戲同時進行,本質是在兩個任務之間切換執行,吃一口飯然后打打游戲,打打游戲再吃一口飯.
多道技術是在不同任務之間切換執行,由于計算機的切換速度非常快,所以用戶是沒有任何感覺的,看起來就像是兩個任務都在執行,但是另一個問題是,僅僅是切換還不行,還需要在切換前保存當前狀態,切換回來時恢復狀態,這些切換和保存都是需花費時間的,如果對于一些不會出現IO操作的程序而言,切換不僅不能提高效率,反而降低效率.
例如:做基本加減乘除運算
第四代計算機:個人計算機
第四代也就是我們常見的操作系統,大多數具備圖形化界面,如:Windows,macOS,CentOS等
四.進程與程序
進程是正在運行的程序,程序是程序員編寫的一堆代碼,也就是一堆字符,當這堆代碼被系統加載到內存中并執行時,就有了進程。
查看進程:在CMD中輸入tasklist查看操作系統所有的進程,在cmd中輸入tasklist | findstr +程序名 查看該程序下的所有進程.
并發與并行
并發指的是多個事件同時發生 例如洗衣服和做飯,同時發生了,但本質上是兩個任務在切換,給人的感覺是同時在進行,也被稱為偽并行
并行指的是多個事件都在進行著 例如:一個人在寫代碼,一個人在寫書,cpu雙核,四核,八核同時運行多個程序,這個是真正的并行
阻塞與非阻塞
阻塞狀態是因為程序遇到了IO操作,或是sleep,導致后續的代碼不能被CPU執行
非阻塞與之相反,表示程序正在正常被CPU執行
進程有三種狀態:就緒態,運行態,和阻塞態 就緒態和阻塞態就是阻塞,運行態是非阻塞.
五.進程的相關理論
進程的創建:
1.系統初始化
2.一個進程在運行過程中開啟了子進程
3,用戶的交互請求,而創建了一個子進程(雙擊暴風影音)
4.一個批處理作業的初始化(只在大型機的批處理系統中使用)
進程的銷毀
正常退出(自愿,如用戶點擊交互式頁面的叉號,或程序執行完畢調用發起系統調用正常退出,在linux中用exit,在windows中用ExitProcess)
出錯退出(自愿,python a.py中a.py不存在)
嚴重錯誤(非自愿,執行非法指令,如引用不存在的內存,1/0等,可以捕捉異常,try...except...)
被其他進程殺死(非自愿,如kill -9)
unix和windows創建進程
相同點:
進程創建后,父進程和子進程有各自不同的地址空間(多道技術要求物理層面實現進程之間內存的隔離),任何一個進程的在其地址空間中的修改都不會影響到另外一個進程。
不同點:
UNIX子進程會將父進程內存地址中的值原模原樣的拷貝到子進程內存中去
windows在創建子進程時不僅會拷貝父進程的數據還會將父進程的代碼再執行一次.導致重復創立多個子進程.所以一定要將創立子進程的代碼放到main(只有右鍵運行它才會運行,加載不會運行)的下面.
六.PID和PIDD
os.getpid() #獲取自己進程的id
os.getppid() #獲取父進程的id
七.開啟進程的兩種方式
方法一:實例化Process
from multiprocessing import Process
import os
def task(name):
print(name)
print('self',os.getpid())
print('parent',os.getppid())
print('這是子進程')
if __name__ == '__main__':
print('self',os.getpid())
print('parent',os.getppid())
p = Process(target = task,name='這是子進程',args=('henry',)) #實例化Process
p.start() #給操做系統發送通知,要求操作系統開啟子進程
方法二:繼承Process 并覆蓋run方法
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,inter):
super().__init__()
self.inter = inter
# 子類中的方法 只有run會被自動執行
def run(self):
print('下載網址%s'%self.inter)
if __name__ == '__main__':
p = MyProcess('www.22ccc.com')
p.start()
八.進程間內存相互隔離
from multiprocessing import Process
import time
a = 100
def task():
global a
a = 0
print('子進程的a:%s'%a) #結果:0
if __name__ == '__main__':
p = Process(target=task)
p.start()
time.sleep(0.5)
print('父進程的a:%s'%a) #結果:100
九.join函數 父進程等待子進程結束
from multiprocessing import Process
import time
def task():
print('上傳文件...')
time.sleep(3)
print('上傳結束...')
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join() #本質上是提高了子進程的優先級,當cpu在切換的時候,優先切換子進程
print('上傳文件成功!')
from multiprocessing import Process
import time
def task(num):
print('這是子進程%s'%num)
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
ps = []
for i in range(5):
p = Process(target=task,args=(i,))
p.start()
ps.append(p)
for p in ps: #將所有進程指令一同發出
p.join()
print(time.time()-start_time)
print('over')
十.Process常用屬性
from multiprocessing import Process
import time
def task():
time.sleep(3)
print('執行完畢')
if __name__ == '__main__':
p = Process(target=task,name='henry')
p.start()
print(p.name) #獲取名字
print(p.is_alive())#子進程沒有結束True,子進程結束False
p.terminate()#終止這個程序
print(p.pid)#獲取進程自己的id
p.daemon #守護進程
十一.孤兒進程和僵尸進程
孤兒進程是指父進程已經終止了,但是自己還在運行,是無害的
孤兒進程會自定過繼給操作系統
僵尸進程是指子進程執行完成所有任務 已經終止了但是還殘留一些信息(進程id 進程名)
但是父進程沒有去處理這些殘留信息 導致殘留信息占用系統內存 僵尸進程時有害的
當出現大量的僵尸進程時 會占用系統資源 可以把它父進程殺掉 僵尸就成了孤兒 操作系統會負責回收數據
總結
- 上一篇: Docker入门二
- 下一篇: 机器视觉之eVision