1. 程式人生 > >python並發編程之多線程

python並發編程之多線程

開始 ted 相同 進入 count init 任務 結束 oba

開啟線程的兩種方式:

技術分享
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello%name)
if__name__==__main__:
    t=Thread(target=sayhi,args=(egon,))
    t.start()
    print(主線程)
方式一 技術分享
from threading import Thread
import time
class Sayhi(Thread):
    def__init__(self,name):
        supper().
__init__() self.name=name def run(self): time.sleep(2): print(%s say hello%self.name) if __name__==__main__: t=Say(egon) t.start() print(主線程)
方式二

在這裏我要說明一下他們誰的開啟速度快

from threading import Thread
from multiprocessing import Process
import os

def work():
    
print(hello) if __name__ == __main__: #在主進程下開啟線程 t=Thread(target=work) t.start() print(主線程/主進程) ‘‘‘ 打印結果: hello 主線程/主進程 ‘‘‘ #在主進程下開啟子進程 t=Process(target=work) t.start() print(主線程/主進程) ‘‘‘ 打印結果: 主線程/主進程 hello

很明顯我們可以看到:在線程裏面會先打印子線程在打印主線程,而在進程裏面會先打印主進程然後打印子進程。(在這裏我想簡單的說一下,就是說你開啟一個進程,你得去重新獲得資源,然而開啟線程的時候,資源已經存在了,不需要去開辟新的資源,所以它的開啟速度就會明顯快了好多)

補充:開啟一個線程,在他之上有一個進程,我們在開啟線程的時候回自動產生一個線程,這個線程就叫做主線程,開啟的線程叫做其他線程(為什麽不叫做子線程呢,就是因為在這裏線程他只是共享資源,他們之間沒有任何的依賴關系)

接下來講一下:同一進程內的線程共享該進程的數據(資源)

from  threading import Thread
from multiprocessing import Process
import os
def work():
    global n
    n=0

if __name__ == __main__:
    # n=100
    # p=Process(target=work)
    # p.start()
    # p.join()
    # print(‘主‘,n) #毫無疑問子進程p已經將自己的全局的n改成了0,但改的僅僅是它自己的,查看父進程的n仍然為100


    n=1
    t=Thread(target=work)
    t.start()
    t.join()
    print(,n) #查看結果為0,因為同一進程內的線程之間共享進程內的數據

(在這裏我要簡單的說明一下,為什麽同一進程內的線程可以共享該進程的數據,從這個實例中我們可以清楚的看到,在進程中,子進程他只是將自己的n改成了0,而父進程的n始終都是100,而對於線程來說,n的結果是0,這就是因為,同一進程內的線程共享該進程的數據)

實例:三個任務,一個接收用戶輸入,一個將用戶輸入的內容格式化成大寫,一個將格式化後的結果存入文件(首先,在這裏面三個任務是同時執行的,)

from threading import Thread
msg_l=[]
format_l=[]
def talk():
    while True:
        msg=input(>>: ).strip()
        if not msg:continue
        msg_l.append(msg)

def format_msg():
    while True:
        if msg_l:
            res=msg_l.pop()
            format_l.append(res.upper())

def save():
    while True:
        if format_l:
            with open(db.txt,a,encoding=utf-8) as f:
                res=format_l.pop()
                f.write(%s\n %res)

if __name__ == __main__:
    t1=Thread(target=talk)
    t2=Thread(target=format_msg)
    t3=Thread(target=save)
    t1.start()
    t2.start()
    t3.start()

線程相關的其他方法:

Thread實例對象的方法
  # isAlive(): 返回線程是否活動的。
  # getName(): 返回線程名。
  # setName(): 設置線程名。

threading模塊提供的一些方法:
  # threading.currentThread(): 返回當前的線程變量。
  # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。
  # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
復制代碼
from threading import Thread
import threading
from multiprocessing import Process
import os

def work():
    import time
    time.sleep(3)
    print(threading.current_thread().getName())


if __name__ == __main__:
    #在主進程下開啟線程
    t=Thread(target=work)
    t.start()

    print(threading.current_thread().getName())
    print(threading.current_thread()) #主線程
    print(threading.enumerate()) #連同主線程在內有兩個運行的線程
    print(threading.active_count())
    print(主線程/主進程)

    ‘‘‘
    打印結果:
    MainThread
    <_MainThread(MainThread, started 140735268892672)>
    [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
    主線程/主進程
    Thread-1
    ‘‘‘

主線程等待子線程結束

復制代碼
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

if __name__ == __main__:
    t=Thread(target=sayhi,args=(egon,))
    t.start()
    t.join()
    print(主線程)
    print(t.is_alive())
    ‘‘‘
    egon say hello
    主線程
    False
    ‘‘‘
復制代碼

守護線程:

無論是進程還是線程,都是:守護xxx會等待主xxx完畢後被銷毀,

主進程與主線程在什麽情況下才算運行完畢

1.主進程在其代碼結束後就已經算運行完畢了,(守護進程就在此時被回收)。主進程會一直等非守護的子進程都運行玩不後回收子進程的資源,(否則會產生僵屍進程),才會結束

2.主線程在其他非守護線程運行完畢後才算運行完畢(守護線程在此時就被回收)。主線程的結束意味著進程的結束,進程整體的資源都被回收,因而主線程必須在其余非守護線程都運行完畢後才能結束

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

if __name__ == __main__:
    t=Thread(target=sayhi,args=(egon,))
    t.setDaemon(True) #必須在t.start()之前設置
    t.start()

    print(主線程)
    print(t.is_alive())
    ‘‘‘
    主線程
    True

八 同步鎖

三個需要註意的點:
#1.分析Lock的同時一定要說明:線程搶的是GIL鎖,拿到執行權限後才能拿到互斥鎖Lock

#2.使用join與加鎖的區別:join是等待所有,即整體串行,而鎖只是鎖住一部分,即部分串行

#3. 一定要看本小節最後的GIL與互斥鎖的經典分析

GIL VS Lock

機智的同學可能會問到這個問題,就是既然你之前說過了,Python已經有一個GIL來保證同一時間只能有一個線程來執行了,為什麽這裏還需要lock?

 首先我們需要達成共識:鎖的目的是為了保護共享的數據,同一時間只能有一個線程來修改共享的數據

然後,我們可以得出結論:保護不同的數據就應該加不同的鎖。

 最後,問題就很明朗了,GIL 與Lock是兩把鎖,保護的數據不一樣,前者是解釋器級別的(當然保護的就是解釋器級別的數據,比如垃圾回收的數據),後者是保護用戶自己開發的應用程序的數據,很明顯GIL不負責這件事,只能用戶自定義加鎖處理,即Lock

過程分析:所有線程搶的是GIL鎖,或者說所有線程搶的是執行權限

  線程1搶到GIL鎖,拿到執行權限,開始執行,然後加了一把Lock,還沒有執行完畢,即線程1還未釋放Lock,有可能線程2搶到GIL鎖,開始執行,執行過程中發現Lock還沒有被線程1釋放,於是線程2進入阻塞,被奪走執行權限,有可能線程1拿到GIL,然後正常執行到釋放Lock。。。這就導致了串行運行的效果

  既然是串行,那我們執行

  t1.start()

  t1.join

  t2.start()

  t2.join()

  這也是串行執行啊,為何還要加Lock呢,需知join是等待t1所有的代碼執行完,相當於鎖住了t1的所有代碼,而Lock只是鎖住一部分操作共享數據的代碼。

python並發編程之多線程