多线程创建
假如我们需要实现以下功能:在一个线程中,每秒循环输出当前的年月日时分秒;于此同时,实现张三的姓名每2秒打印输出4次结束。
如果我们不使用多线程操作,可能会写如下代码
import time
import datetime
def Time01():
now_time = datetime.datetime.now()
print (now_time)
time.sleep(1)
def Cat():
print("张三")
time.sleep(0.5)
while(1):
Cat()
Time01()
但是这样以来不管我们想要睡眠多长时间,打印的结果都会是一前一后的输出出来,原因就在于我们在没有创建线程之前默认使用的是主函数所创建的主线程。而一个线程无法同时执行多个程序。
接下来我们就利用多线程进行功能的实现,首先是线程的创建:
import datetime
import time
import threading
#普通实现
def Time01():
while(1):
now_time = datetime.datetime.now()
print (now_time)
time.sleep(1)
def Cat():
while(1):
print("张三")
time.sleep(0.5)
t01=threading.Thread(target=Time01)
t02=threading.Thread(target=Cat)
线程创建需要先引入threading,创建threading中的Thread类,用一个变量名接收类创建的对象,小括号中target就是需要执行的函数名。完成上述操作后我们所需要的两个线程就创建好了。
调用线程只需要使用start函数就可以了
t01.start()
t02.start()
当然我们也可以用类的方法实现多线程:
import datetime
import time
import threading
# 类的实现
class Mythread01(threading.Thread):
def __init__(self):
super(Mythread01,self).__init__()
def run(self):
while(1):
now_time = datetime.datetime.now()
print (now_time)
time.sleep(1)
class Mythread02(threading.Thread):
def __init__(self):
super(Mythread02,self).__init__()
def run(self):
while(1):
print("张三")
time.sleep(0.5)
main.py:
from demo01 import *
def main():
Mt01=Mythread01()
Mt01.start()
Mt02=Mythread02()
Mt02.start()
if __name__=="__main__":
main()
多线程特性
- join() #多线程暂停,如果启动多个线程时,多个线程是同时启动的,利用join函数可以使当前线程结束后再开启下一个线程
-
setDaemon() #守护线程,当主线程结束时,所有线程随之结束
- threading.active_count() #查看活动的线程个数
-
threading.current_thread() #查看当前线程
线程锁
在多线程操作中,有可能会遇到两个线程同时操作一个变量的情况,这时就要利用线程锁来保证当前线程所操作的内容被锁定
import threading
def run():
lock.acquire() #在操作数值之前申请一把锁
global x
x +=1
lock.release() #操作完毕后把锁释放
'''
线程15,线程16可能会同时拿到 x=5这个数据,打印结果将会是99
'''
if __name__=="__main__":
x=0
res=[]
lock=threading.Lock() #实例化一把锁
for i in range(100):
t=threading.Thread(target=run)
t.start()
res.append(t)
for t in res:
t.join()
print(x) #打印结果为100
递归锁
一般情况下我们使用线程锁就可以保证当前线程所操作的内容被锁定,但是当所要同时多个线程保护不同的内容时,线程锁不能起到很好的作用,下面我们看一个案例
import threading
def run01():
lock.acquire()
global x
x+=1
lock.release()
return x
def run02():
lock.acquire()
global y
y+=1
lock.release()
return y
def run03():
lock.acquire()
func01=run01()
func02=run02()
lock.release()
print(run01,run02)
if __name__=="__main__":
x=0
y=0
lock=threading.Lock()
for i in range(5):
t=threading.Thread(target=run03)
t.start()
while threading.active_count() != 1:
print(f"当前有{threading.active_count()}个线程运行")
print("程序运行结束")
可以看到在程序运行过程中,程序会被锁死,原因在于程序运行到for循环中,创建了一个线程然后进入函数run03,执行函数run03时创建了一个锁,然后调用run01函数再次创建一个锁,程序接着运行到lock.release()时,由于之前创建了两个锁,程序不知道要释放哪一个锁,所以运行到这里的话程序就被锁死无法继续运行了
但是可以发现由于我们只实例化了一个锁,所以我们大胆猜想是否可以实例化不同的锁,然后调用创建和释放,这样由于我们指明了具体的是那个锁,程序就可以识别了
import threading
def run01():
global x
lock01.acquire()
x+=1
lock01.release()
return x
def run02():
global y
lock02.acquire()
y+=1
lock02.release()
return y
def run03():
lock03.acquire()
func01=run01()
func02=run02()
lock03.release()
print(func01,func02)
if __name__=="__main__":
x=0
y=0
lock01=threading.Lock()
lock02=threading.Lock()
lock03=threading.Lock()
for i in range(50):
t=threading.Thread(target=run03)
t.start()
while threading.active_count() != 1:
print(f"当前有{threading.active_count()}个线程运行")
print("程序运行结束")
既然解决了多个锁调用时出现的指向不明的问题,但是这样写仍然存在一定的问题,就是如果我们不是只有三个函数,而是有许多函数,那么我们就要创基lock01,lock02,lock03等等等,这样写未免有些太麻烦了,有没有简单一点的写法呢,答案是当然有,只需要把threading.Lock()变成threading.RLock()就可以了
import threading
def run01():
global x
lock.acquire()
x+=1
lock.release()
return x
def run02():
global y
lock.acquire()
y+=1
lock.release()
return y
def run03():
lock.acquire()
func01=run01()
func02=run02()
lock.release()
print(func01,func02)
if __name__=="__main__":
x=0
y=0
lock=threading.RLock()
for i in range(50):
t=threading.Thread(target=run03)
t.start()
while threading.active_count() != 1:
print(f"当前有{threading.active_count()}个线程运行")
print("程序运行结束")
最终我们打印输出我们想要的结果。