1、多任務
有很多的場景中的事情是同時進行的,比如開車的時候 手和腳共同來駕駛汽車,再比如唱歌跳舞也是同時進行的。
程序中模擬多任務:
import time
import threading
def sing():
for i in range(3):
print("正在唱歌...%d"%i)
time.sleep(1)
def dance():
for i in range(3):
print("正在跳舞...%d"%i)
time.sleep(1)
if __name__ == '__main__':
# sing()
# dance()
t1 = threading.Thread(target=sing) # 注意裏面的方法不要加()
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
"""
正在唱歌...0
正在跳舞...0
正在唱歌...1
正在跳舞...1
正在唱歌...2
正在跳舞...2
"""
– 並行:真的多任務 cpu大於當前執行的任務
– 併發:假的多任務 cpu小於當前執行的任務
上圖中的執行順序是隨機的,也可能不執行某個程序等等。
import threading
import time
def demo():
print("hello world") # 子線程
time.sleep(1)
if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=demo) # 創建線程
t.start() # 開始執行demo方法 主線程 主線程會等到子線程執行結束之後。主線程纔會結束
"""
hello world
hello world
hello world
hello world
hello world
"""
注意:主線程會等到子線程執行結束之後。主線程纔會結束
驗證一下:
import threading
import time
def demo():
for i in range(5):
print("hello...")
time.sleep(1) # 延遲1s
if __name__ == '__main__':
t = threading.Thread(target=demo)
t.start()
print("執行到這裏主線程代碼結束")
"""這裏說明主線程代碼結束,但是線程沒有結束,需要等到子線程都結束才行
hello...
執行到這裏主線程代碼結束
hello...
hello...
hello...
hello...
""" 這裏先執行了主線程的程序因爲sleep函數。一般延遲 1s 已經能夠執行完主線程剩餘的代碼
守護線程
t.setDaemon(True)
import threading
import time
def demo():
for i in range(5):
print("hello...")
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo)
# 守護線程
t.setDaemon(True) # 不會等子線程結束,主線程就已經結束了
t.start()
print("執行到這裏主線程代碼結束")
""" 不會等子線程結束,主線程就已經結束了
hello...
執行到這裏主線程代碼結束
"""
會等到子線程結束,主線程纔會執行和結束
t.join()
import threading
import time
def demo():
for i in range(5):
print("hello...")
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo)
t.start()
# 會等到子線程結束,主線程纔會執行和結束
t.join()
print("執行到這裏主線程代碼結束")
""" 會等到子線程結束,主線程纔會執行和結束
hello...
hello...
hello...
hello...
hello...
執行到這裏主線程代碼結束
"""
查看線程數量
enumerate()函數的使用
In [1]: name = ['a', 'b', 'c']
In [2]: for i in name:
...: print(i)
...:
a
b
c
In [3]: for temp in enumerate(name):
...: print(temp)
...:
(0, 'a')
(1, 'b')
(2, 'c')
In [4]: for i, temp in enumerate(name):
...: print(i, temp)
...:
0 a
1 b
2 c
threading.enumerate() 查看當前線程的數量
import threading
import time
def demo1():
for i in range(5):
print("demo1----%d" % i)
time.sleep(1) # 不添加延遲的話,最後的輸出語句中只有主線程
def demo2():
for i in range(5):
print("demo2----%d" % i)
time.sleep(1) # 不添加延遲的話,最後的輸出語句中只有主線程。因爲子線程都已經執行完了,不會輸出了
def main():
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo1)
t1.start()
t2.start()
# 獲取當前程序所有的線程
print(threading.enumerate()) # 當前的程序所運行的線程
if __name__ == '__main__':
main()
import threading
import time
def demo1():
for i in range(5):
print("demo1----%d" % i)
time.sleep(1)
def demo2():
for i in range(5):
print("demo2----%d" % i)
# time.sleep(1)
def main():
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo1)
t1.start()
t2.start()
while True:
# 獲取當前程序所有的線程
print(threading.enumerate())
if len(threading.enumerate()) == 1: # 如果只有一個線程,跳出當前循環 break
break
time.sleep(1)
if __name__ == '__main__':
main()
上面一段修改後的代碼執行結果如下:
能清晰地看出來enumerate():查看 當前 線程的數量的含義
驗證子線程的執行與創建
– 當調用Thread的時候,不會創建線程。
– 當調用Thread創建出來的實例對象的start方法的時候,纔會創建線程以及開始運行這個線程。
import threading
import time
def demo1():
for i in range(5):
print("demo1----%d" % i)
def main():
print(threading.enumerate())
t1 = threading.Thread(target=demo1) # 不會創建線程
print(threading.enumerate())
t1.start() # 創建線程,並且執行
print(threading.enumerate())
if __name__ == '__main__':
main()
'''
[<_MainThread(MainThread, started 6636)>]
[<_MainThread(MainThread, started 6636)>] # 經過threading.Thread也沒有創建線程
demo1----0
[<_MainThread(MainThread, started 6636)>, <Thread(Thread-1, started 10188)>] # 經過start之後,創建了線程
demo1----1
demo1----2
demo1----3
demo1----4
'''
繼承Thread類創建線程
import threading
import time
class A(threading.Thread):
# def __init__(self, name):
# super().__init__(name=name)
def run(self): # 這裏必須使用run方法,名稱也不能改變,纔是類創建的線程
for i in range(5):
print(i)
def demo(self):
print("demo")
if __name__ == "__main__":
t = A()
t.start() # 這裏的start方法,是從父類中繼承的方法 threading源碼中有start方法
t.demo() # 這裏不是多線程的方式,只是累調用了方法
"""
0
demo
1
2
3
4
"""
多線程共享全局變量(線程間通信)
修改全局變量一定需要加global嗎?
In [5]: a = [1, 2]
In [6]: id(a)
Out[6]: 1793113290120 # id爲1793113290120
In [7]: a = a + [3]
In [8]: a
Out[8]: [1, 2, 3]
In [9]: id(a)
Out[9]: 1793124586888 # a=a+改變了id 修改了指向空間中的數據
In [10]: a.append(4)
In [11]: a
Out[11]: [1, 2, 3, 4]
In [12]: id(a)
Out[12]: 1793124586888 # append沒有改變id 沒有修改指向空間中的數據
In [13]: a += 5
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-13-79ab3c32a247> in <module>
----> 1 a += 5
TypeError: 'int' object is not iterable
In [14]: a +=[5]
In [15]: a
Out[15]: [1, 2, 3, 4, 5]
In [16]: id(a) # +=也沒有改變id 沒有修改指向空間中的數據
Out[16]: 1793124586888
上面案例的意思是:
a +=1 與 a=a+1 雖然在數值運算上面一樣的結果,但是代表的含義是不一樣的,指向內存空間中的數據改變了
num = 100
l = [11, 22]
def demo():
global num
num += 100 # int類型爲不可變類型,無法修改指向空間的數據
def demo1():
l.append(33) # 沒有修改指向空間的數據,就不需要使用global
def demo2():
global l
l = l + [44] # 修改了指向空間的數據,就需要使用global
print(num) # 100
demo()
print(num) # 200
demo1()
print(l) # [11, 22, 33]
demo2()
print(l) # [11, 22, 33, 44]
多線程共享全局變量
案例如下說明:
多線程是共享全局變量的
import threading
import time
num = 100
def demo():
global num
num += 100
print("demo----%d" % num)
def demo1():
print("demo1---%d" % num)
def main():
t = threading.Thread(target=demo)
t1 = threading.Thread(target=demo1)
t.start()
time.sleep(1)
t1.start()
time.sleep(1)
print("demo1----%d" % num)
if __name__ == '__main__':
main()
多線程參數-args
threading.Thread(target=test, args=(num,))
import threading
import time
num = [11, 22]
def demo(num):
num.append(33)
print("demo----%s" % str(num)) # demo----[11, 22, 33]
def demo1(num):
print("demo1----%s" % str(num)) # demo1----[11, 22, 33]
def main():
t = threading.Thread(target=demo, args=(num,)) # args=(num,) 加,是爲了表達爲元祖類型參數
t1 = threading.Thread(target=demo1, args=(num,))
"""threading.Thread的源碼中包含args=()參數,且爲元組類型,所以傳參也要是元祖
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
"""
t.start()
# time.sleep(1)
t1.start()
# time.sleep(1)
print("main----%s" % str(num)) # main----[11, 22, 33]
if __name__ == '__main__':
main()
需要注意的是:# args=(num,) 中加,逗號是爲了表達參數爲元祖類型