Python 程序與子程序,執行緒與子執行緒
一、什麼是程序
顧名思義,程序即正在執行的一個過程。程序是對正在執行程式的一個抽象。(執行程式三大執行元件:記憶體,磁碟,CPU。程式就是一堆程式碼,放在磁盤裡面,在執行程式時,程式碼載入到記憶體,由CPU到記憶體取程式碼,最終程式執行起來。這就是起了一個程序。)
程序的概念起源於作業系統,是作業系統最核心的概念,也是作業系統提供的最古老也是最重要的抽象概念之一。作業系統的其他所有內容都是圍繞程序的概念展開的。所以想要真正瞭解程序,必須事先了解作業系統,點選此處
1、作業系統的作用:
1).隱藏醜陋複雜的硬體介面,提供良好的抽象介面
2).管理、排程程序,並且將多個程序對硬體的競爭變得有序
2、多道技術:
1).產生背景:針對單核,實現併發
現在的主機一般是多核,那麼每個核都會利用多道技術
有4個cpu,運行於cpu1的某個程式遇到io阻塞,會等到io結束再重新排程,會被排程到4個
cpu中的任意一個,具體由作業系統排程演算法決定。
2).空間上的複用:如記憶體中同時有多道程式
3).時間上的複用:複用一個cpu的時間片
強調:遇到io切,佔用cpu時間過長也切,核心在於切之前將程序的狀態儲存下來,這樣才能保證下次切換回來時,能基於上次切走的位置繼續執行
二、如何使用程序
2.1、程序的建立與銷燬
1)、程序的建立:程序建立在作業系統裡。
2)、程序的銷燬:作業系統不做這件事了,把程序所佔用的資源釋放掉。
2.2、開啟子程序的2種方式 用到-multiprocessing 模組
方式一:
from multiprocessing import Process #匯入multiprocessing模組,然後匯入Process這個類 import time def task(x): #父程序 print('%s is run....' %x)time.sleep(3) print('%s is done...' %x) if __name__ == '__main__': # windows下開子程序必須在"main"下,因為在建立子程序時,會呼叫createprocess模組(會把父程序的內容導一遍,把父程序裡面的資料完完整整的生成一份給子程序) # 在linux系統下,不會有這個問題(兩種系統造子程序的方式不一樣) p=Process(target=task,args=('子程序1',)) # 傳參方式一: # 因為匯入的Process是一個類,所以需要例項化 # target:起的程序需要執行哪一段程式碼(這裡指task)# args:傳參的方式,這裡是一個元組(括號內需要加括號) # p=Process(target=task,kwargs={'x':'任務1'}) # 傳參方式二: # kwargs:按照字典的方式進行傳參,key是函式的引數'x',value是引數對應的值。 p.start() # 給作業系統發訊號,告訴作業系統有一個子程序要起來,讓作業系統去申請記憶體空間,然後把父程序裡面的資料拷貝一個給子程序,然後呼叫CPU來執行。 # 子程序被造出來後, 和父程序就沒有任何關係,因為程序之間是隔離的 print('主程序') >>:主程序 >>:子程序1 is run.... >>:子程序1 is done...
方式二:
from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name=name def run(self): print('%s is runing' %self.name) time.sleep(3) print('%s is done' %self.name) if __name__ == '__main__': p=MyProcess('sudada') p.start() print('主程序') >>:主程序 >>:sudada is runing >>:sudada is done
2.3、基於主程序開啟的子程序,PID一定是不同的。主程序開啟後,會等到子進執行完畢,主程序才會關閉。
from multiprocessing import Process import time import os def task(): print('%s 子程序 is run....' %os.getpid()) time.sleep(3) print('%s 子程序 is done...' %os.getpid()) if __name__ == '__main__': p=Process(target=task,) p.start() print('主程序:',os.getpid()) >>:主程序: 15944 >>:15860 子程序 is run.... >>:15860 子程序 is done...
2.4、程序的PID與PPID (相同的程式執行多次,等於開啟了多個程序(每個程序的PID不同))
2.4.1、PID (程序的ID),PPID (程序的父程序)
from multiprocessing import Process import time import os def task(): print('子程序的PID:%s,子程序的PPID:%s' %(os.getpid(),os.getppid())) if __name__ == '__main__': p=Process(target=task,) p.start() print('主程序PID:',os.getpid()) >>:主程序PID: 16660 >>:子程序的PID:16916,子程序的PPID:16660可以看出,子程序的PPID就等於父程序的PID!
2.5、程序物件的join方法 (主程序在等待子程序執行完畢之後,在執行下一行程式碼 ( 而不是主程序先執行程式碼,等待子程序執行結束 ) )
1、單個子程序:
from multiprocessing import Process import time def task(x): print('%s is run....' % x) time.sleep(3) print('%s is done...' % x) if __name__ == '__main__': p1 = Process(target=task, args=('子程序',)) p1.start() p1.join() # 等待"p.start()"執行完畢,也就是主程序在等待子程序執行完畢之後,在執行下一行程式碼。 print('主程序') >>:子程序 is run.... >>:子程序 is done... >>:主程序>>:2、多個子程序並行執行:
from multiprocessing import Process import time import os def task(n): print('%s 子程序 is run....' %os.getpid()) time.sleep(n) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2=Process(target=task,args=(2,)) p3=Process(target=task,args=(3,)) p1.start() p2.start() p3.start() # 同時開啟p1,p2,p3三個子程序(3個子程序並行執行) p1.join() p2.join() p3.join() # 等待p1,p2,p3三個子程序執行完畢後,執行下一行程式碼 print('主程序') >>:9540 子程序 is run.... >>:17340 子程序 is run.... >>:2100 子程序 is run.... >>:主程序
2.6、程序物件其他相關的屬性或方法
1、自定義程序名稱 p.name
from multiprocessing import Process import time import os def task(n): print('子程序的PID:%s' %os.getpid()) if __name__ == '__main__': p=Process(target=task,args=(3,),name='子程序') #修改程序的名稱為"子程序" p.start() print(p.name) # 列印程序名稱,預設為"Process-1" >>:子程序 >>:子程序的PID:12244
2、判斷程序是否處於活動狀態 p.is_alive()
from multiprocessing import Process import time import os def task(n): print('子程序的PID:%s' %os.getpid()) if __name__ == '__main__': p=Process(target=task,args=(3,),name='子程序') # 修改程序的名稱為"子程序" p.start() print(p.is_alive()) # 判斷程序是否處於一個活動狀態 print('主') >>:True >>:主 >>:子程序的PID:14572
3、終止子程序 p.terminate()
from multiprocessing import Process import time import os def task(n): print('子程序的PID:%s' %os.getpid()) if __name__ == '__main__': p=Process(target=task,args=(3,),name='子程序') # 修改程序的名稱為"子程序" p.start() p.terminate() # 給操作體統傳送指令,把程序釋放掉 time.sleep(1) # 應用程式不能直接終止掉程序,需要等待作業系統終止程序 print(p.is_alive()) # 判斷程序是否處於一個活動狀態 print('主') >>:False >>:主
2.7、程序之間記憶體空間相互隔離
from multiprocessing import Process import time import os x=100 # 主程序的x=100 def task(): global x x=0 # 子程序的x=0 if __name__ == '__main__': p=Process(target=task) p.start() p.join() print('主',x) # 列印子程序的變數x >>:主 100 # x依舊為100,說明程序之間相互隔離,相互之間的變數不會受到影響
三、殭屍程序與孤兒程序
3.1、殭屍程序
一個主程序下存在多個子程序(a子程序,b子程序,c子程序),當(a,b,c)任意一個子程序掛掉後,不會把子程序的所有資訊都回收(會回收子程序所佔用的CPU,記憶體,開啟的檔案數等資源),但是(CPU佔用時間,子程序的PID)等資訊會被留下。--這就屬於殭屍程序(子程序不佔CPU,記憶體了,但是子程序的所有資料還沒有被完全回收)。 所有的子程序都會經歷殭屍程序這麼一個過程。這種狀態是為了能讓父程序在任何時刻都能檢測到子程序相關的資訊。
殭屍程序在什麼狀態下會消失掉:
當父程序掛掉之後,這種狀態就會消失!父程序在掛掉之前會發起一個系統呼叫(waitPID),會把所有子程序的PID資訊全部都回收掉,那麼此時子程序的所有資訊將全部被回收。
殭屍程序的危害:
當子程序掛了,父程序沒有掛掉,並且父程序一直不傳送回收子程序的相關資訊的操作,就會產生大量的殭屍程序。大量的殭屍程序會佔用PID號,導致正常的程序啟動會受影響。
殭屍程序的回收:
1、殺掉殭屍程序的父程序
linux下檢視殭屍程序:
ps aux | grep Z #zombie
top #檢視zombie有幾個
3.2、什麼是孤兒程序
當子程序正常,但是父程序掛掉的時候,那麼父程序下的所有子程序都屬於孤兒程序。這些孤兒程序會被init程序回收。
四、執行緒與多執行緒
4.1、什麼是執行緒 (執行緒沒有主次之分)
執行緒就是一條流水線額工作過程,一個程序內至少有一個執行緒(可以有多個執行緒)。
程序只是一個資源單位;
執行緒才是真正的執行單位。執行緒也就是程式碼的執行過程!
如果把作業系統比喻成一個工廠的話,那麼每造一個程序就相當於在工廠內開了一個車間,那麼每造一個執行緒就相當於在車間(程序)內開了一條流水線。CPU真正執行的是程序裡面的執行緒! --每個車間(程序)內至少有一個流水線(執行緒)。
4.2、如何使用執行緒(開啟程序的2種方式)
def abc(name): print('%s is start' %name) time.sleep(random.randint(1,3)) print('%s is end' %name) if __name__ == '__main__': t=Thread(target=abc,args=('sudada',)) t.start() #執行緒開啟的速度非常快,幾乎在向作業系統傳送開啟執行緒指令時,執行緒就已被開啟print('主') >>:sudada is start #這裡看到在執行t.start()之後,立馬就列印了'sudada is start' >>:主 >>:sudada is end方式二:
from threading import Thread import time,random class Mythread(Thread): def run(self): print('%s is start' %self.name) time.sleep(random.randint(1,3)) print('%s is end' %self.name) if __name__ == '__main__': t=Mythread() t.start() print('主') >>:Thread-1 is start >>:主 >>:Thread-1 is end
4.3、程序與執行緒的區別
1、同一程序下的多個執行緒,共享該程序內的資料。
2、不同的程序內的多個執行緒,資源無法共享,因為程序之間的記憶體是相互隔離的。
3、程序與執行緒的系統資源佔比,建立執行緒所佔用的系統資源遠遠小於建立程序所佔用的系統資源(100倍差值)。
4.4、同一程序下的多個執行緒,共享該程序內的資料
from threading import Thread x=100 def task(): global x x=0 if __name__ == '__main__': t=Thread(target=task) t.start() t.join() print('主',x) >>:主 0
4.5、執行緒相關的其他方法
1、判斷執行緒是否處於活動狀態 t.is_alive()
from threading import Thread def task(name): print('%s is running ' %name) if __name__ == '__main__': t=Thread(target=task,args=('sudada',)) t.start() print(t.is_alive()) # 執行緒開啟後,判斷執行緒是否存活(由於程序(作業系統)控制執行緒的回收,所以此處是否被回收取決於程序(作業系統)的回收狀態) t.join() print(t.is_alive()) # 主執行緒等待子執行緒執行完畢後檢視是否存活,這裡一定為"False" print('主') >>:sudada is running >>:False >>:False >>:主2、自定義執行緒名稱 t.setName('執行緒1')
from threading import Thread def task(name): print('%s is running ' %name) if __name__ == '__main__': t=Thread(target=task,args=('sudada',)) t.start() t.join() print('主') print(t.getName()) #列印執行緒名稱"Thread-1" t.setName('執行緒1') #設定執行緒名稱為"執行緒1" print(t.getName()) #列印執行緒名稱得到"執行緒1" >>:Thread-1 >>:執行緒13、列印當前執行緒的名稱
from threading import Thread,current_threaddef task(): print('%s is running ' %current_thread().getName()) if __name__ == '__main__': t=Thread(target=task,) t.start() print('主') >>:Thread-1 is running >>:主4、一個程序內的所有執行緒的PID相同