python高級編程學習——07—(線程、並行併發、守護線程:setDaemon(True) 、線程的join()、查看線程數量:enumerate()、多線程共享全局變量(線程間通信)、線程傳參)

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,) 中加,逗號是爲了表達參數爲元祖類型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章