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