Python線程詳解

Python線程詳解

# 進程的三狀態:就緒 運行 阻塞
    # multiprocessing模塊
        # Process-開啓進程
        # Lock - 互斥鎖
            # 爲何要在進程中加鎖
                # 由於進程操做文件也會發生數據不安全
        # Queue -隊列 IPC機制(Pipe,redis,memcache,rabbitmq,kafka)
            # 生產者消費者模型
        # Manager - 提供數據共享機制

------------------Process-開啓進程----------
可查看連接https://www.cnblogs.com/Marcki/p/10111927.html
---------線程學習--------
一、啓動線程start
線程的異步html

二、開啓多個子線程
三、join方法
四、測試
進程和線程的開啓效率差
數據隔離仍是共享
五、守護線程python


1)啓動線程start

(1從線程導入線程 2實例化Thread,傳遞線程函數。3對象.start())redis

```
import os
from threading import Thread
#multiprocessing徹底是仿照threading類寫的
def func():
print(os.getpid())
#啓動線程 start
Thread(target=func).start()
print('---->',os.getpid())
-------------結果:
5804
----> 5804安全

```
#由上可知,一、主線程和子線程在同一個進程裏,pid一致。二、在Windows裏面不須要寫if name=='main'了。三、先打印子線程裏的內容,再打印主線程裏內容,與進程相反,說明線程開啓快,快到主線程還沒執行下一句代碼這個子線程就建立並執行了。多線程

2)線程是異步的,併發的

```併發

import os,time
from threading import Thread
def func():
print('start son thread')
time.sleep(1)
print('end son thread')
Thread(target=func).start()
print('start',os.getpid())
time.sleep(0.5)
print('end',os.getpid())
--------------結果:
start son thread
start 5280
end 5280
end son threadapp

```異步

3)開啓多個子線程

import os,time
from threading import Thread
def func():
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid())
for i in range(3):
Thread(target=func).start()
----------------結果:
start son thread
start son thread
start son thread
end son thread 6772
end son thread 6772
end son thread 6772ide

4)往線程函數裏傳參,經過args,和進程同樣。線程的調度仍然是操做系統決定的,誰的時間片到了就運行誰的

```函數

import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(3):
Thread(target=func,args=(i,)).start()
------------結果:
start son thread
start son thread
start son thread
end son thread 7112 0
end son thread 7112 1
end son thread 7112 2

```

5)一、主線程等待全部子線程結束以後才結束。二、主線程若是結束了,主進程也就結束了。

import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(3):
Thread(target=func,args=(i,)).start()
print("main")
--------------結果:
start son thread
start son thread
start son thread
main #主線程代碼執行完了,等待子線程結束
end son thread 5416 0
end son thread 5416 1
end son thread 5416 2
Process finished with exit code 0 #全部子線程結束,主線程才結束,而後主進程結束
5)[1]join方法 阻塞 直到子線程執行結束
#用join,那麼線程對象和start就不要放在一塊兒了,單執行對象.start()來開啓
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
t=Thread(target=func,args=(0,))
t.start()
t.join()
print("子線程執行結束!")
----------------------結果:
start son thread
end son thread 6756 0
子線程執行結束!
[2]建立多個子線程,t.join()在循環外,這時t表明for循環最後一個值3,全部只是阻塞最後一個建立的t對象。因此每次打印「子線程執行結束!」都是在「end son thread 4864 3」以後。可是這時執行慢的子線程就沒被阻塞就開始執行主線程中代碼了。
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
for i in range(4):
t=Thread(target=func,args=(i,))
t.start()
t.join()
print("子線程執行結束!")
--------------結果:
start son thread
start son thread
start son thread
start son thread
end son thread 4864 0
end son thread 4864 2
end son thread 4864 3
子線程執行結束!
end son thread 4864 1
[3]保證全部子線程都結束以後再繼續執行主線程中的代碼
#注:將全部線程對象都追加到列表,循環列表,將每一個對象.join阻塞。全部線程對象執行結束而後才執行主線程中程序
import os,time
from threading import Thread
def func(arg):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid(),arg)
li=[]
for i in range(4):
t=Thread(target=func,args=(i,))
t.start()
li.append(t)
for t in li:t.join()
print("子線程執行結束!")
--------------結果:
start son thread
start son thread
start son thread
start son thread
end son thread 6480 0
end son thread 6480 1
end son thread 6480 2
end son thread 6480 3
子線程執行結束!

6)[1]使用面向對象的方式啓動線程

#注意:用本身類建立的對象記得start開啓線程
#一、繼承Thread類二、定義run方法,run方法是線程執行函數
import os,time
from threading import Thread
class MyThread(Thread):
def run(self):
print('start son thread')
time.sleep(1)
print('end son thread',os.getpid())
for i in range(3):
MyThread().start()
------------------結果:
start son thread
start son thread
start son thread
end son thread 6116
end son thread 6116
end son thread 6116
[2]面向對象啓動線程並往線程函數裏傳參
#一、建立init方法,傳參進入類中 二、執行父類的init方法,不執行報錯入下
import os,time
from threading import Thread
class MyThread(Thread):
def init(self,name):
super().init() #執行父類init方法在實例變量後面報錯了,不知道怎麼回事,有時間測試一下
self.name=name

def run(self):
    time.sleep(1)
    print('end son thread',os.getpid(),self.name)

for i in range(3):
MyThread("mcw").start()
------------------結果:
end son thread 2060 mcw
end son thread 2060 mcw
end son thread 2060 mcw

不執行父類init報錯:
assert self._initialized, "Thread.init() not called"
AssertionError: Thread.init() not called

[3]start執行的原理
class Foo:
def start(self):
self.run()
def run(self):
print('run in Foo')
class Son(Foo):
def run(self):
print('run in son')
Son().start()
---------------結果:
run in son
[4]自定義類中把init方法去掉後,實例化的時候還帶有傳參報錯
t=MyThread("mcw")
File "C:\python3\lib\threading.py", line 780, in init
assert group is None, "group argument must be None for now"
AssertionError: group argument must be None for now

7)線程裏面的其它方法 查看線程id

[1]用類來啓動線程,類裏面和外面查看線程的id。對象.ident self.ident。注意:在init裏定義與父類相同的實例變量名,會被覆蓋掉的。
import os,time
from threading import Thread
class MyThread(Thread):
def run(self):
time.sleep(1)
print(' thread id',self.ident)
for i in range(3):
t=MyThread()
t.start()
print("線程id",t.ident)
[2]current_thread()在哪一個線程裏表明哪一個線程。用函數來啓動線程,查看線程id 。
#當前線程current_thread()對象,記得加括號(),函數中調用對象
import time
from threading import current_thread,Thread
def func(i):
print('start son thread',i,current_thread(),type(current_thread()))
print("線程函數中調用執行這個函數的線程對象的線程id:%s"%current_thread().ident)
time.sleep(1)
print('end son thread',)
t=Thread(target=func,args=(1,))
t.start()
print("main",t,t.ident)
--------------結果:
start son thread 1 <Thread(Thread-1, started 4444)> <class 'threading.Thread'>
線程函數中調用執行這個函數的線程對象的線程id:4444
main <Thread(Thread-1, started 4444)> 4444
end son thread

from threading import current_thread,Thread
def func():
print('end son thread',current_thread().ident)
t=Thread(target=func).start()
print("main thread",current_thread().ident)
------------結果:
end son thread 1936 #current_thread()在哪一個線程裏表明哪一個線程
main thread 4928
[3]enumerate active_count
from threading import current_thread,Thread,active_count,enumerate
def func():
print('end son thread',current_thread().ident)
t=Thread(target=func).start()
print(enumerate(),enumerate) #主線程和子線程兩個。enumerate()表示當前運行的線程,
print(active_count(),active_count) #active_count()表明當前運行的線程的個數,至關於len(enumerate())
-------------------結果:
end son thread 4324
2
[<_MainThread(MainThread, started 2460)>, <Thread(Thread-1, started 4324)>] <function enumerate at 0x006E1270>
2 <function active_count at 0x006E11E0>
[4]terminate 能結束進程 。
在線程中不能從主線程結束一個子線程

8)進程和線程的開啓效率差。進程通常開cpu個數的1到2倍,開多了浪費系統資源

import time
from threading import Thread
from multiprocessing import Process
def func(a,b):
c=a+b
if name=="main":
p_start=time.time()
p_li=[]
for i in range(100):
p=Process(target=func,args=(i,i2))
p.start()
p_li.append(p)
for p in p_li:p.join()
print("進程:",time.time()-p_start)
t_start=time.time()
t_li = []
for i in range(100):
t=Thread(target=func,args=(i,i
2))
t.start()
t_li.append(t)
for t in t_li:t.join()
print("線程:",time.time() - t_start)
----------------結果:
進程: 6.1583521366119385
線程: 0.01500082015991211 #幾百倍速度差距

9)線程共享進程中的數據。子線程在進程中共享數據,

from threading import Thread
n=100
def func():
global n #最好不要輕易修改全局變量
n-=1
t_li=[]
for i in range(100):
t=Thread(target=func)
t_li.append(t)
t.start()
for t in t_li:t.join()
print(n)
-----------結果:
0

10)守護線程

[1]沒有守護線程的時候
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.start()
---------結果:
一直打印「in son1」
[2]有守護線程的時候,線程開啓後,後面沒有運行一段時間的代碼
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
---------------結果:
什麼都沒有打印
#這說明後面沒代碼了,主線程結束守護線程就結束了
[3]有守護線程的時候,線程開啓後,後面有運行一段時間的代碼time.sleep(3)
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
----------結果:
打印5次"in son1"後結束
#這說明後面還有代碼,主線程執行完代碼結束後守護線程就結束了
[4]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3.1)
---------結果:
打印6次"in son1"後結束
#和上面打印5次對比,5次是由於主線程執行的3秒內,守護線程一直在運行,守護線程0.5秒打印一次,3秒到了主線程結束守護線程3秒也就結束。守護線程每0.5秒後打印一次,3秒後由於主線程中止守護線程也中止因此沒有打印第6次
#這裏將主線程運行時間+0.1,也就是主線線程運行時間大於3秒,那麼守護線程也運行3秒以上,剛過3秒就運行到打印「in son1」,因此打印6次
[5]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
def son2():
for i in range(5):
time.sleep(1)
print("in son2")
t=Thread(target=son1)
t.daemon=True
t.start()
Thread(target=son2).start()
time.sleep(3)
-----------結果:
in son1
in son2
in son1
.......
"in son2"打印5次,"in son1"打印9次
#這說明守護線程一直等到全部的線程都結束以後才結束的
#這說明守護線程除了守護主線程的代碼以外也會守護子線程
#主線程代碼執行完畢,主線程沒有關閉。主線程等待全部子線程結束以後才結束。守護線程守護主線程,因此守護線程也要等全部子線程結束以後才結束。若是子線程都結束了,主線程還有程序在運行,那麼守護線程仍是在守護主線程。
[6]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
print('1')
print('1')
print('1')
----------結果:
六次 「in son1」
1
1
1
#緣由:爲何主線程有代碼在運行的時候,守護線程沒有作打印操做了呢,由於這裏守護線程是0.5s打印一次,主線程打印3次「1」所花費的時間不足0.5s不夠守護線程到達下一次打印的時間。爲何打印6次呢,由於主線程sleep3秒正好達到守護線程打印第6次的時間,在主線程第3秒到打印出第一個「1」這個時間之間,守護線程纔打印出第6個「in son1」。time.sleep(3)後面沒有代碼,守護線程在第3秒是沒有執行到打印就隨着主線程中止而中止了的。
[7]
import time
from threading import Thread
def son1():
while True:
time.sleep(0.5)
print("in son1")
t=Thread(target=son1)
t.daemon=True
t.start()
time.sleep(3)
print('1')
time.sleep(1)
print('1')
---------------結果:
六個「in son1」 而後:
1
in son1
in son1
1

11)在多進程裏啓動多線程

import os
from multiprocessing import Process
from threading import Thread

def tfunc():
print('tfunc線程',os.getpid())
def pfunc():
print('pfunc進程-->',os.getpid())
Thread(target=tfunc).start()

if name == 'main':
Process(target=pfunc).start()
------------------結果:
pfunc進程--> 9716
tfunc線程 9716

方法總結: 開啓進程執行進程函數 進程函數裏開啓線程執行線程函數 線程函數裏執行線程運行的程序

相關文章
相關標籤/搜索