1. 程式人生 > 其它 >python多程序與多執行緒

python多程序與多執行緒

技術標籤:pythonpython程式語言

程序與執行緒

程序是資源分配的最小單位,他是作業系統進行資源分配和排程執行的基本單位,一旦建立一個程序就會分配一定的資源,通俗理解一個正在執行的程式就是一個程序,例如微信,QQ都是程序。
執行緒是程式執行的最小單位,實際上程序只負責分配資源,而利用這些資源執行程式的是執行緒,一個程序中至少有一個執行緒來執行程式。同一程序中的執行緒共享程序的全部資源。
1.關係對比:執行緒是依附在程序裡面的,沒有程序就沒有執行緒,一個程序預設提供一條執行緒,程序可以建立多個執行緒。
2.區別對比:建立程序的資源開銷比較大,程序是作業系統資源分配的基本單位,執行緒是CPU排程的基本單位。

3.優缺點:程序可以使用多核,但資源開銷大。執行緒資源開銷小,不能使用多核。

多執行緒——Threading

執行緒是程式執行的最小單位。

1.Python中的GIL

在這裡插入圖片描述

又叫全域性直譯器鎖,每個執行緒在執行的過程中都需要先獲取GIL,保證同一時刻只有一個執行緒在執行,目的是解決多執行緒同時競爭程式中的全域性變數而出現的執行緒安全問題。也就是說多執行緒下每個執行緒在執行的過程中都需要先獲取GIL,保證同一時刻只有一個執行緒在執行,即在多核CPU中,多執行緒同一時刻也只有一個執行緒在執行。看似是多執行緒實際上是單執行緒。它並不是python語言的特性,僅僅是由於歷史的原因在CPython直譯器中難以移除,因為python語言執行環境大部分預設在CPython直譯器中。

2.計算密集型和IO密集型

計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視訊進行高清解碼等等。這種計算密集型任務雖然也可以用多工完成,但是花費的主要時間在任務切換的時間,此時CPU執行任務的效率比較低。
IO密集型:涉及到網路請求(time.sleep())、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。

3.總結

1.Python語言和GIL沒有什麼關係。僅僅是由於歷史原因在Cpython虛擬機器(直譯器),難以移除GIL。

2.GIL:全域性直譯器鎖。每個執行緒在執行的過程都需要先獲取GIL,保證同一時刻只有一個執行緒可以執行程式碼。
3.執行緒釋放GIL鎖的情況: 在IO操作等可能會引起阻塞的system call之前,可以暫時釋放GIL,但在執行完畢後,必須重新獲取GIL Python 3.x使用計時器(執行時間達到閾值後,當前執行緒釋放GIL)或Python 2.x,tickets計數達到100。
4.Python使用多程序是可以利用多核的CPU資源的。
5.多執行緒爬取比單執行緒效能有提升,因為遇到IO阻塞會自動釋放GIL鎖。
6. GIL本質是一把互斥鎖,將執行緒從併發變為序列,犧牲效率保證資料安全。
7. 不同的資料要加不同的互斥鎖,GIL保護的是直譯器級別的安全。
8.應用場合
對於計算密集型,多程序優於多執行緒。對於IO密集型,多執行緒優於多程序。

4.解決GIL問題的方案:

1.使用其它語言,例如C,Java
2.使用其它直譯器,如java的直譯器jython
3.使用多程序

2.多執行緒的基本語法

檢視執行緒名字,數量

import threading
def thread_job():
    print("當前執行緒名字:",threading.current_thread())
    
def main():
    add_thread=threading.Thread(target=thread_job())
    print(threading.active_count())#顯示的執行緒數量
    print(threading.enumerate())#枚舉出來的執行緒物件列表
    print(threading.current_thread())#當前的執行緒物件
    print(threading.current_thread().name)  # 當前的執行緒物件名字

if __name__ == '__main__':
    main()

2.帶引數,並且有返回值

import threading
import time

def job1(data,list1):
    for i in data:
        list1.append(i**2)
    return list1

def main():
    List=[]
    thread1 = threading.Thread(target=job1,args=(range(5),List) ,name="job1")
    thread2 = threading.Thread(target=job1, args=(range(10,15), List), name="job1")
    thread1.start()
    thread2.start()
    thread1.join()#等執行緒執行完之後在執行主執行緒的語句
    thread2.join()
    return List

if __name__ == '__main__':
    time_start=time.time()
    print(main())
    time_end = time.time()
    print('totally cost', time_end - time_start)

3.無引數,無返回值,守護主執行緒

import threading
import time

def job1():
    print("job1,開始")
    time.sleep(3)
    print("job1,完了")
def job2():
    print("job2,開始")
    for i in range(10):
        print(i)
        time.sleep(0.2)
    print("job2,完了")

def main():#子執行緒
    thread1 = threading.Thread(target=job1,name="job1")
    thread2 = threading.Thread(target=job2, name="job2")
    # 守護主執行緒:主執行緒結束後,子執行緒無論是否執行完了都要結束。
    #方法一
    # thread1 = threading.Thread(target=job1, name="job1", daemon=True)
    # 方法二
    # thread1.setDaemon(True)
    # thread2.setDaemon(True)

    thread1.start()
    thread2.start()
    thread1.join()#等執行緒執行完之後在執行主執行緒的語句
    thread2.join()
    
if __name__ == '__main__':#主執行緒
    main()
    print("任務全部結束")

4.執行緒池(開啟執行緒池後,每次開啟4個執行緒,也就是一次只執行4個請求)

import time
import threadpool

def job(url):
    time.sleep(3)
    print(str(url)+"\n")

if __name__ == '__main__':
    urls=[i for i in range(7)]
    pool=threadpool.ThreadPool(4)#建立執行緒池,開啟4個執行緒
    request=threadpool.makeRequests(job,urls)
    for re in request:
        pool.putRequest(re)
    pool.wait()

5.簡單的執行緒

import threading
import time

def job1():
    print(threading.current_thread())  # 當前的執行緒物件名字
    print("job1,開始")
    time.sleep(3)
    print("job1,完了")

if __name__ == '__main__':#主執行緒
   time1=time.time()
   t=[]
   for i in range(5):
       thread1 = threading.Thread(target=job1, name="job1")
       t.append(thread1)
       thread1.start()
   for t1 in t:
       t1.join()
   print("執行時間:",time.time()-time1)

6.繼承方法寫入

import time
import threading
class MyThread(threading.Thread):
    def __init__(self,A,value):
        threading.Thread.__init__(self)
        self.value=value
        self.A = A

    def run(self):
        self.A.append(self.value)
        print((self.name,self.A))

if __name__ == '__main__':
    A=[]
    t1=time.time()
    lis=[]
    for i in range(5):
        t=MyThread(A,i)
        lis.append(t)
        t.start()
    for i in lis:
        i.join()
    print("******************")
    print(time.time()-t1)
    print(A)

7.當多個執行緒共享一個引數變數的時候,需要對其數值進行修改需要,新增鎖,在某個執行緒訪問的時候,其他執行緒不能訪問。

死鎖

1.程式記憶體在問題,導致鎖不能及時被釋放(增加try except finally方法)
2.線上程共享多個資源的時候,如果兩個執行緒分別佔有一部分資源的時候,並且同時想要獲取對方的資源,就會造成死鎖。

避免死鎖的方法

1.銀行家演算法
2.新增等待時間

多程序

import multiprocessing
import threading
import  time
import os
def sing(name):
    print("sing程序編號",os.getpid())#子程序編號
    print("sing父程序編號", os.getppid())  # 父程序編號
    for i in range(3):
        print(name+"sing..")
        time.sleep(1)
def dance(name):
    print("dance程序編號",os.getpid())#子程序編號
    print("dance父程序編號", os.getppid())  # 父程序編號
    for i in range(3):
        print(name+"dance..")
        time.sleep(1)

def job(name):
    for i in range(3):
        time.sleep(0.2)
        with open('ts.txt', 'a', encoding='utf-8') as f:
            text = str(i)+name+"sing\n"
            f.write(text)

if __name__ == '__main__':
    print("主程序編號", os.getpid())  # 程序編號
    sing_process=multiprocessing.Process(target=sing,args=("曉明",))#元組傳參,守護主程序,方法1
    dance_process=multiprocessing.Process(target=dance,args=("曉明",))#元組傳參
    # dance_process.daemon=True#守護主程序(主程序執行完了,子程序也停止)方法2
    sing_process.start()
    dance_process.start()
    print("主程序執行完了")
    #建立多個程序
    t1=time.time()
    list1 = ["1", "2", "3"]
    ls=[]
    for str1 in list1:
        process = multiprocessing.Process(target=job, args=(str1))
        # process = threading.Thread(target=job, args=(str1))
        ls.append(process)
        process.start()
    for jjj in ls:
        jjj.join()
    print("時間:",time.time()-t1)