python多任务4-1
多线程
- 1 什么是多线程呢?
每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。
助记:多线程就是一个程序在运行的时候,可以执行它自己的多个代码,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。
再比如我们吃饭,一般先吃菜后喝粥,这个时候来个多线程。我们喝一口粥吃一口菜,就这样最后吃完。(图中的粥字错了)
- 2 python代码实现
1 首先我们要导入包 threading
import threading
创建一个对象
def 函数名1(num1):
while True:
pass
def 函数名2(num2):
while True:
pass
def main();
# 再target中添加要执行的函数,args传入一个列表,也就是参数
t1 = threading.Thread(target = 函数名1, args=[num1])
t2 = threading.Thread(target = 函数名2, args=[num2])
# 下面我们开启线程就可以了
t1.start()
t2.start()
友情提示:
我们在这里可以查看线程的数量
print("线程个数:", len(threading.enumerate()))
对于for num, t in enumerate(threading.enumerate()):
print("---", num, t)
这个就是一个拆包,对于列表的拆包。
再类中的使用:
import threading
class MyThread(threading.Thread):
# 必须重写run方法线程进入其中执行
def run(self) ->None:
for i in range(5):
print(i)
pass
def main():
# 开启类,调用Thread创建出来的实例对象的start()方法地时候才会创建线程并让其运行。
t = MyThread()
# 当实例对象调用start() 方法的时候就会调用run方法
t.start()
多线程中的全局变量
在一个函数中对全局变量进行修改时,是否用global要看是否对全局变量的执行指向进行了修改
1、如果改了执行,全局变量指向一个新的地方必须加global
2 、如果仅仅是修改了指向的空间中的数据此时不用必须是使用global
extend 原地修改列表,只可添加可迭代对象,待添加的对象有多少个就往原对象中添加多少个
append 原地修改列表,可添加任何对象,但无论是什么对象,在末尾添加,而且只算一个元素
+= 原地修改列表,只可执行列表之间的操作,效果上相当于extend
+ 会拷贝成一个新对象,而且只能执行列表之间的操作,性能问题要考虑
num = 10
def sum():
num += 20
我们知道整型数据是不可变数据类型,也就是我们要num+10的时候这个时候它
的指向会变所以我们要用global这个关键字。
def sum():
global num
num += 20
- 3 注意事项
同步的概念: 在计算机中,同步就是按照预定的先后顺序进行运行
比如:你说完我再说
异步: 与同步相反,就是同时说,你也说,我也说。- 在运用多线程的时候容易出现的问题,有些事情必须等我干完你才能再干的。比如你给你女朋友买棒棒糖,只有等你买过来了才能吃,如果你正在去买的路上,这个时候你的女朋友说赶紧给我棒棒糖,这个时候肯定你拿不出啊。所以这个时候我们需要一个“{锁}”也就是如果你买糖需要5分钟,那么你可以给你女朋友说5分钟之后才有的吃。5分钟之内不要打扰我。这就可以实现了你先买,她后吃。
代码解说:
- 在运用多线程的时候容易出现的问题,有些事情必须等我干完你才能再干的。比如你给你女朋友买棒棒糖,只有等你买过来了才能吃,如果你正在去买的路上,这个时候你的女朋友说赶紧给我棒棒糖,这个时候肯定你拿不出啊。所以这个时候我们需要一个“{锁}”也就是如果你买糖需要5分钟,那么你可以给你女朋友说5分钟之后才有的吃。5分钟之内不要打扰我。这就可以实现了你先买,她后吃。
import threading
num =0
def sum1(s1):
global num
for i in range(s1):
num+=1
print("s2",num)
def sum2(s2):
global num
for i in range(s2):
num+=1
print("s2",num)
def main():
t1 = threading.Thread(target = sum1, args=[100000])
t2 = threading.Thread(target = sum2, args=[100000])
t1.start()
t2.start()
if __name__ == "__main__":
main()
运行结果:
我们可以看到运行的结果num <2000000
为什么小于200000呢,如图所示:
怎么解决这种现象呢,这里我们就想到了用一种互斥锁,就是说当我执行某些关键性的代码的时候,这个时候资源只能由我用,其他人不能用。
代码实现:
1创建互斥锁
mutex = threading.Lock()
2 锁定
mutex.acquire()
3 释放
mutex.release()
改进后代码:
import threading
num =0
def sum1(s1):
global num
for i in range(s1):
mutex.acquire()
num+=1
mutex.release()
print("s2",num)
def sum2(s2):
global num
for i in range(s2):
mutex.acquire()
num+=1
mutex.release()
print("s2",num)
mutex = threading.Lock()
def main():
t1 = threading.Thread(target = sum1, args=[1000000])
t2 = threading.Thread(target = sum2, args=[1000000])
t1.start()
t2.start()
if __name__ == "__main__":
main()
运行结果:
死锁
这个资源锁定但是新的问题又来了,一种情景就是有两把锁A和B
def lock1():
#启用锁A
mutexA.acquire()
time.sleep(1)
# 启用锁B
mutexB.acquire()
def lock2():
#启用锁B
mutexB.acquire()
time.sleep(1)A
# 启用锁A
mutexA.acquire()
mutexA = threading.lock()
mutexB = threading.lock()
由以上可以看出我们函数1
图解:
解决此问题:
1 程序设计的时候要避免(银行家算法这个自己研究一下,这里不讲了,有点抽象)
2 添加超时时间。时间到了我就释放资源什么的。