多线程
线程
python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
使用threading模块
# coding=utf-8
import threading
import time
def sing():
for _ in range(10):
print("正在唱歌")
time.sleep(1)
def dance():
for _ in range(10):
print("正在跳舞")
time.sleep(1)
def main():
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=sing)
t1.start()
t2.start()
if __name__ == "__main__":
main()
查看线程数量
# coding=utf-8
import threading
from time import sleep,ctime
def sing():
for _ in range(3):
print("正在唱歌")
sleep(1)
def dance():
for _ in range(3):
print("正在跳舞")
sleep(1)
if __name__ == "__main__":
print("---开始---:%" % ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
while True:
length = len(threading.enumerate())
print('当前运行的线程数为:%d' % length)
if length <= 1:
break
sleep(0.5)
线程-注意点
1.线程执行代码的封装
通过使用threading模块能够完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Tread 就可以了,然后重写run方法。这种方式可以在一个线程里面执行多个函数,其他函数放在run里面执行
#coding=utf-8
import htreading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm" + self.name + '@' + str(i) # name属性中保存的是当前线程的名字
print(msg)
if __name__ == "__main__":
t = MyThread()
t.start() # 会执行类里面run()方法,如果没有run方法,则运行出错
多线程-全局共享变量
from threading import Thread
import time
g_num = 0
def work1():
global g_num
for i in range(3):
g_num += 1
print("---in work1,gum is %d---" % g_num)
def work2():
global g_num
print("---in work2,g_num is %d" % g_num)
print("---线程创建之前g_num is %d" % g_num)
t1 = Thread(target=work1)
t1.start()
time.sleep(1)
t2 = Thread(target=work2)
t2.start()
列表当做实参传递到线程中
from threading import Thread
import time
def work1(nums):
nums.append(44)
print("--in work1--", nums)
def work2(nums):
# 延迟一会,保证t1线程中的事情做完
time.sleep(1)
print("--in work2--", nums)
g_nums = [11, 22, 33]
t1 = Thread(target=work1, args=(g_nums,))
t1.start()
t2 = Thread(target=work2, args=(g_nums,))
t2.start()
多线程-共享全局变量问题
多线程开发可能遇到的问题
假设两个线程t1和t2都要对全局变量g_num(默认是0)进行加1运算,t1和t2都各对g_num加10次,g_num的最终的结果应该为20
但是由于多线程的同时操作,有可能出现下面情况:
1.在g_num=0时,t1取得g_num=0.此时系统把t1调度为"sleeping"状态,把t2转换为"running"状态,t2也获得g_num=0
2.然后t2对得到的值进行加1并赋给g_num,使得g_num=1
3.然后系统又把t2调度为"sleeping",把t1转为"running".线程t1又把它之前得到的0加1后赋值给g_num.
4.这样导致虽然t1和t2都对g_num加1,但结果仍然是g_num=1
同步的概念
同步就是协同步调,按预定的先后次序进行运行
解决线程同时修改全局变量的方式
- 系统调用t1,然后获取到g_num的值为0,此时上一把锁,既不允许其他线程操作g_num
- t1对g_num的值进行+1
- t1解锁,此时g_num的值为1,其他线程就可以使用g_num了,而且是g_num的值不是0,而是1
- 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,既保证了数据的正确性
互斥锁
import threading
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
''' 注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞
如果在调用acquire对这个锁上锁之前,它就已经被其他线程上了锁,囊额此时acquire会堵塞,知道这个锁被解锁为止
上锁的代码越少越好
'''
示例:
import threading
g_num = 0
mutex = threading.Lock()
def work1():
global g_num
for _ in range(1000000):
mutex.acquire()
g_num += 1
mutex.release()
def work2():
global g_num
for _ in range(1000000):
mutex.acquire()
g_num += 1
mutex.release()
def main():
t1 = threading.Thread(target=work1)
t2 = threading.Thread(target=work2)
t1.start()
t2.start()
print(g_num)
if __name__ == "__main__":
main()
死锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁
避免死锁
- 程序设计是要尽量避免(银行家算法)
- 添加超时时间等
浙公网安备 33010602011771号