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,) 中加,逗号是为了表达参数为元祖类型

发布了50 篇原创文章 · 获赞 9 · 访问量 2079

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103948676