二.併發編程 (程序中線程操做)

一 .線程的建立(Threading.Thread)

1.threading模塊

  Python提供了幾個用於多線程編程的模塊,包括thread、threading和Queue等。thread和threading模塊容許程序員建立和管理線程。thread模塊提供了基本的線程和鎖的支持,
threading提供了更高級別、功能更強的線程管理的功能。Queue模塊容許用戶建立一個能夠用於多個線程之間共享數據的隊列數據結構。   避免使用thread模塊,由於更高級別的threading模塊更爲先進,對線程的支持更爲完善,並且使用thread模塊裏的屬性有可能會與threading出現衝突;
其次低級別的thread模塊的同步原語不多(實際上只有一個),而threading模塊則有不少;再者,thread模塊中當主線程結束時,全部的線程都會被強制結束掉,
沒有警告也不會有正常的清除工做,至少threading模塊能確保重要的子線程退出後進程才退出。   thread模塊不支持守護線程,當主線程退出時,全部的子線程不論它們是否還在工做,都會被強行退出。而threading模塊支持守護線程,守護線程通常是
一個等待客戶請求的服務器,若是沒有客戶提出請求它就在那等着,若是設定一個線程爲守護線程,就表示這個線程是不重要的,在進程退出的時候,不用等待這個線程退出。
multiprocess模塊的徹底模仿了threading模塊的接口,兩者在使用層面,有很大的類似性,於是再也不詳細介紹(官方連接)

 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.start()
    print('主線程')

執行:
主線程
egon say hello
import threading,time


def run():
    print("子線程(%s)開始"%(threading.current_thread().name))
    # 實現線程功能
    print("打印了")
    time.sleep(2)
    print("子線程(%s)結束"%(threading.current_thread().name))

if __name__ == '__main__':

# 任何進程默認就會啓動一個線程 稱爲主線程 主線程能夠器動新的子線程 # current_thread() #返回當前線程的實例 在哪一個線程裏面就返回哪一個線程的實例
    print("主線程(%s)啓動"%(threading.current_thread().name)) # 打印當前線程名稱
    # 01建立子線程 
     # target=run執行指定代碼
     # name 是指線程名稱
    t=threading.Thread(target=run,name="rooTHread")
    # 線程執行
    t.start()
    print("主線程(%s)結束"%(threading.current_thread().name)) # 打印當前線程名稱

執行:
    主線程(MainThread)啓動
    子線程(rooTHread)開始
    主線程(MainThread)結束
    打印了
    子線程(rooTHread)結束
import threading,time
aa=100000000000000000000000
def run():
    print("子線程(%s)開始"%(threading.current_thread().name))
    # 實現線程功能
    print("打印了")
    time.sleep(2)
    print(aa)
    print("子線程(%s)結束"%(threading.current_thread().name))


if __name__ == '__main__':

    print("主線程(%s)啓動"%(threading.current_thread().name)) # 打印當前線程名稱
   
    t=threading.Thread(target=run,name="rooTHread")

    # 線程執行
    t.start()
    # 等待線程結束
    t.join()
    print("主線程(%s)結束"%(threading.current_thread().name)) # 打印當前線程名稱

# 主線程(MainThread)啓動
# 子線程(rooTHread)開始
# 打印了
# 100000000000000000000000
# 子線程(rooTHread)結束
# 主線程(MainThread)結束
from  threading import Thread
def  run(n):
    print(11111)
    print(n)
p=Thread(target=run,args=("子線程",))
print("主線程!!!!!")
p.start()
 使用類來啓動多線程

class
Mythread(Thread): def __init__(self,aa): super().__init__() # 繼承線程 self.aa=aa def run(self): print("111111111") print(self.aa) def run1(self): print("哈哈哈哈哈哈") p=Mythread("子線程") p.start() p.run1()

3. 多線程與多進程

 
 
啓動多線程
from  threading import Thread
def  run(n,i):
    print(n,"個數",i)
for i in  range(20):
    print("主線程!!!!!")
    p=Thread(target=run,args=("子線程",i))
    p.start()
 
 
啓動多線程和多進程
import time,os
from multiprocessing import Process
from  threading import Thread
def work():
    print('hello')

if __name__ == '__main__':
        # 在主進程下開啓線程
        t = Thread(target=work)
        t.start()
        print('主線程/主進程')
''' 打印結果: hello 主線程/主進程 ''' # 在主進程下開啓子進程 t = Process(target=work) t.start() print('主線程/主進程')
''' 打印結果: 主線程/主進程 hello '''
線程和進程pid的比較
import time,os
from multiprocessing import Process
from threading import Thread

def work():
print('hello',os.getpid())

if __name__ == '__main__':
#part1:在主進程下開啓多個線程,每一個線程都跟主進程的pid同樣
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主線程/主進程pid',os.getpid(),"22222222222222222222222222222222222222")

# 執行結果
hello22340
hello22340
主線程 / 主進程pid2234022222222222222222222222222222222222222


print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@2")
#part2:開多個進程,每一個進程都有不一樣的pid
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主線程/主進程pid',os.getpid(),"111111111111111111111111111111111111111")

執行結果
主線程/主進程pid 22340 111111111111111111111111111111111111111
hello 20204
hello 14772
 
開啓多個進程和多個線程時間比較

import
time,os from multiprocessing import Process from threading import Thread def fun2(n): print("進程/線程-->%s"%n,os.getpid()) if __name__=="__main__": # 進程 star=time.time() print("主進程", os.getpid()) # part1:在主進程下開啓多個線程,每一個線程都跟主進程的pid同樣 for i in range(50): p=Process(target=fun2,args=(i,)) p.start() p.join() t2=time.time()-star print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") # 線程 print("主線程", os.getpid()) # part2:開多個進程,每一個進程都有不一樣的pid st= time.time() for i in range(50): aa= Thread(target=fun2,args=(i,)) aa.start() aa.join() t3=time.time()-st print("進程執行時間爲%s----線程執行時間爲%s"%(t2,t3)) # 進程執行時間爲5.123770236968994----線程執行時間爲0.015601158142089844

 4.線程數據共享(全局變量)

注意多線程內部有本身的數據棧 數不共享

注意: 多線程和多進程最大的不一樣在於 多進程中 同一個變量 各自有一份拷貝存在每一個進程中 互相不影響 而多線程 全部變量都有全部線程共享 因此任何一個變量均可以被任意 一個線程修改 所以 線程之間共享數據最大的危險在於多個線程同時修改一個變量 容易吧內容改亂 
import time,os
from multiprocessing import Process
from  threading import Thread

aa=66666
def work(n):
    global aa
    aa=10
    aa=n+aa
    print('hello',os.getpid())   # hello 18892
    #print('hello',os.getpid(),id(aa))   # hello 18892   2006806592

if __name__ == '__main__': print(aa) # 66666 這裏仍是打印的全局aa t1=Thread(target=work,args=(10,)) t1.start() print(aa) # 20 print(aa) # 20
# print(id(aa))  2006806592
print(aa) # 20
 
 
所以 線程之間共享數據最大的危險在於多個線程同時修改一個變量
    容易吧內容改亂
import threading,time
aa=0
def run(n):
  for i in range(1000000):
  
    global aa
    aa=aa+n     #15=9+5
    aa=aa-n      #9

if __name__ == '__main__':
     t1=threading.Thread(target=run,args=(6,))
     t2=threading.Thread(target=run,args=(9,))
     t1.start()
     t2.start()
     t1.join()
     t2.join()

     print("aa:",aa)

# 線程1  aa=aa+6         # aa-6=3
# 線程2  aa=aa+9
           # 3-9=-6
解決線程數據共享紊亂 枷鎖
"""
兩個線程同時工做 一個存錢 一個取錢 可能致使數據異常 思路:加鎖 能解決數據亂的問題 """ import threading, time # 鎖對象 lock = threading.Lock() aa = 0 def run(n): for i in range(5): global aa # 加鎖 # 確保了這段代碼只能由一個線程從頭至尾的完整性 從而保持了數據不會亂 # 加鎖由於阻止了多線程的併發 包含鎖的某段代碼實際上只能以單線程模式執行 # 從而效率下降了 要比單線程快點 # 因爲他能夠存在多個鎖 不一樣線程持有不一樣的鎖 有可能試圖獲取其它的鎖 # 可能形成死鎖 致使多個線程掛起 只能靠操做系統了強制終止 lock.acquire() try: aa = aa + n # 15=9+5 aa = aa - n # 9 finally: # 修改完必定要釋放鎖 lock.release() if __name__ == '__main__': t1 = threading.Thread(target=run, args=(6,)) t2 = threading.Thread(target=run, args=(9,)) t1.start() t2.start() print("aa:", aa) t1.join() t2.join() print("aa:", aa) """# 與上面的代碼功能相同 with look 能夠自動上鎖 解鎖 # 只是這樣能夠下降死鎖的機率 with lock: aa=aa+n aa=aa-n """

多線程局部變量之threading.local()用法

假如,開了十個線程而且作一樣的一件事,他們須要帶着本身的數據進來,完成事情後帶着本身的數據出去。若是是併發,同時進來,他們的數據就會混亂。html

通常狀況,咱們加鎖就能夠了,一我的先進來,先加鎖,另外一我的過來看到加鎖了,就在外面等,等裏面的人出來,本身進去加鎖,這樣就不會出現數據混亂的問題。程序員

另外一種解決方法就是threading.local()來解決問題。編程


import
threading num=0 """建立一個全局的thraedlocal 每一個線程有獨有的存儲空間 每一個線程threading對象均可以讀寫 可是互不影響""" local=threading.local() def run(x,n): # 每一個線程都有local.x 就是線程的局部變量 x=x+n print(id(x),x,1111111111111111111111111111111111111111111111111111111) x=x-n print(id(x),x,22222222222222222222222222222222222222222222222222222) def func(n): local.x=num run(local.x,n) print(threading.current_thread().name,local.x) if __name__ == '__main__': t1=threading.Thread(target=func,args=(6,)) t1.start() t1.join() print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") t2 = threading.Thread(target=func, args=(9,)) t2.start() t2.join() print("aa:",num) 2006806144 6 1111111111111111111111111111111111111111111111111111111 2006805952 0 22222222222222222222222222222222222222222222222222222 Thread-1 0 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2006806240 9 1111111111111111111111111111111111111111111111111111111 2006805952 0 22222222222222222222222222222222222222222222222222222 Thread-2 0 aa: 0

from threading import Thread
import time

from threading import Thread
ret = -1  # 先定義一個變量
def task(arg):  # 寫個任務,加個參數
    global ret  # 聲明ret全局變量
    ret = arg  # 每個線程進來到要改這個變量
    print(ret)  # 每一個線程來,改了ret,而後取ret的值

for i in range(10):  # i是線程的值,0 1 2 3 4 5 6 7 8 9
    t = Thread(target=task, args=(i,))  # 開10個線程
    t.start()
    t.join()
print("主線程",ret)  #9


print("****************************************88")

這個程序開了10個線程,每一個線程都執行了更改ret的值並獲取ret更改後的值,若是很是快,他們取到的值都不同.
ret =1  # 先定義一個變量
def task(arg):  # 寫個任務,加個參數
    global ret  # 聲明ret全局變量
    ret = arg  # 每個線程進來到要改這個變量
    time.sleep(2)
    print(ret)  # 每一個線程來,改了ret,而後取ret的值
for i in range(10):  # i是線程的值,0 1 2 3 4 5 6 7 8 9
    t = Thread(target=task, args=(i,))  # 開10個線程
    t.start()
# 打印結果 9 9 9 9 9 9 9 9 9 9

print("****************************************88")

from threading import Thread
from threading import local
import time
# 這是一個特殊的對象
ret = local()  # 先實例化一個對象
def task(arg):  # 寫個任務,加個參數
    ret = arg  # 每個線程進來都給他開闢一個獨立的空間  單純的threading.local()的做用就是這個
    time.sleep(2)
    print(ret)  # 每一個線程來,改了ret,而後取ret的值
for i in range(10):  # i是線程的值,0 1 2 3 4 5 6 7 8 9
    t = Thread(target=task, args=(i,))  # 開10個線程
    t.start()
# 打印結果 0 3 2 5 7 9 8 4 1 6
threading.local()的做用就是爲每一個線程開闢一個獨立的空間進行數據存儲。

 5.線程其餘方法

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

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

def work():
    print('%s is running'%current_thread().getName())
if __name__ == '__main__':
    t=Thread(target=work)  # 線程
    t.start()
    # t.join()
    print(t.is_alive())  # 返回線程是否活動的。
    print(t.getName())   # 返回線程名
    print(current_thread().getName())   # 當前線程名
    print(enumerate()) # 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。
    print(activeCount())  # 返回正在運行的線程數量
    print('主線程')
    
    
"""Thread-1 is running
False
Thread-1
MainThread
[<_MainThread(MainThread, started 12956)>]
1
主線程
"""

6. 線程守護

守護線程
不管是進程仍是線程,都遵循:守護xx會等待主xx運行完畢後被銷燬。須要強調的是:運行完畢並不是終止運行 #1.對主進程來講,運行完畢指的是主進程代碼運行完畢 #2.對主線程來講,運行完畢指的是主線程所在的進程內全部非守護線程通通運行完畢,主線程纔算運行完畢


#1 主進程在其代碼結束後就已經算運行完畢了(守護進程在此時就被回收),而後主進程會一直等非守護的子進程都運行完畢後回收子進程的資源(不然會產生殭屍進程),纔會結束, #2 主線程在其餘非守護線程運行完畢後纔算運行完畢(守護線程在此時就被回收)。由於主線程的結束意味着進程的結束,進程總體的資源都將被回收,而進程必須保證非守護線程都運
行完畢後才能結束。
import threading,os,time
def func1(n):
     while True:
        print(n,"----子線程一",os.getpid())
        time.sleep(0.5)

def func2(n):
    print(n,"-----子線程2",os.getpid())
    time.sleep(1)
if __name__ == '__main__':

     t1=threading.Thread(target=func1,args=(6,))
     t1.daemon=True
     t1.start()

     t2=threading.Thread(target=func2,args=(9,))
     t2.start()
     print("主線程!!!!!!!!", os.getpid())

6 ----子線程一 17140
9 -----子線程2 17140
主線程!!!!!!!! 17140
6 ----子線程一 17140服務器

from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")
def bar():
    print(456)
    time.sleep(3)
    print("end456")

t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------")

# 123
# 456
# main-------
# end123
# end456
from  multiprocessing import Process
import os,time
def func1(n):
    while True:
        print(n, "----子線程一", os.getpid())
        time.sleep(0.5)

def func2(n):
    print(n,"-----子線程2",os.getpid())


if __name__ == '__main__':

     print("主進程!!!!!!!!",os.getpid())
     t1=Process(target=func1,args=(6,))
     t1.daemon=True
     t1.start()

     t2=Process(target=func2,args=(9,))
     t2.start()

 # t1.daemon=True 守護進程 # :守護進程會在主進程代碼執行結束後就終止 # 進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止
相關文章
相關標籤/搜索