1、多线程
①举例:有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的
②定义:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
1.1 模拟多任务
import time
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()
# 只有当唱歌结束了,跳舞才开始,为了让他们同时进行所以有了线程
2、主线程和子线程的执行关系
主线程会等待子线程结束之后在结束
join() 等待子线程结束之后,主线程继续执行
setDaemon() 守护线程,不会等待子线程结束
# 通过函数 使用threading模块中的 Thread类来创建线程
# 通过类 继承threading模块中的 Thread类并实现run()方法
import threading
import time
def demo():
for i in range(5):
print('hello 子线程') # 子线程
time.sleep(1)
if __name__ == '__main__':
# 参数一 group 线程组 None
# 参数二 target 要执行的方法
# 参数三 name 线程名
t = threading.Thread(target=demo) # 创建线程? 面试题
# 守护线程 不会等待子线程结束
# t.setDaemon(True)
# 主线程会等待子线程执行结束之后,主线程在结束
t.start() # 开启线程
# 等待子线程结束之后,后面的主线程在去继续执行,不与子线程抢资源
t.join()
# time.sleep(6)
# print(1)
3、查看线程数量
threading.enumerate() 查看当前线程的数量
复制代码可以观看线程数量,实践方法
import threading
import time
def demo1():
for i in range(5):
print('demo1...%d'%i)
time.sleep(2)
def demo2():
for i in range(10):
print('demo2...%d' % i)
time.sleep(2)
def main():
t1 = threading.Thread(target=demo1, name='demo1')
t2 = threading.Thread(target=demo2, name='demo2')
t1.start()
t2.start()
# t2.join()
# t1.join()
# while True:
# print(threading.enumerate())
# if len(threading.enumerate()) <= 1:
# break
# time.sleep(1)
if __name__ == '__main__':
main()
4、验证子线程的执行与创建
4.1threading.thread
使用threading模块中的 Thread类来创建线程
import threading
import time
def demo():
for i in range(5):
print('demo..%d'%i)
time.sleep(1)
def main():
print(threading.enumerate()) # 一个主线程
t1 = threading.Thread(target=demo,name='demo') # 创建? 证明它不是创建线程
print(threading.enumerate()) # 2 or 1 只有一个主线程
t1.start() # 创建线程并启动线程
print(threading.enumerate()) # 2 or 1 有2个线程 一个是主线程一个是子线程
if __name__ == '__main__':
main()
4.2继承Thread类重写run()方法
import threading
import time
class A(threading.Thread):
def run(self):
for i in range(5):
print(i)
if __name__ == '__main__':
a = A()
print(threading.enumerate()) # [<_MainThread(MainThread, started 9320)>]
time.sleep(3)
a.start()
print(threading.enumerate()) # [<_MainThread(MainThread, started 9320)>, <A(Thread-1, started 1856)>]
time.sleep(3)
print(threading.enumerate()) # [<_MainThread(MainThread, started 7780)>]
5、线程间的通信(多线程共享全局变量)
5.1多线程参数—args
import time
import threading
num = [1,2]
def demo1(nums):
num.append(nums)
print('demo1中的num%s'%str(num))
def demo2():
print('demo2中的num%s'%str(num))
def main():
t1 = threading.Thread(target=demo1,args=(10,))
t2 = threading.Thread(target=demo2)
t1.start()
t1.join()
t2.start()
if __name__ == '__main__':
main()
5.2线程共享全局变量
# a = 20
# def fn():
# global a
# a = 10
# print('函数内部a:%d'%a)#10
#
# print('函数外部a:%d'%a)#20
# fn()
# print('函数外部a:%d'%a)#10
import threading
import time
num = 100
def demo1():
global num
num += 1
print('demo1 - num:%d'%num)
def demo2():
print('demo2 - num:%d'%num)
def main():
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo2)
t1.start()
t1.join()
t2.start()
if __name__ == '__main__':
main()
#结果
#demo1 - num:101
#demo2 - num:101
5.3线程之间的资源竞争
import threading
import time
num = 0
def demo1(nums):
global num
for i in range(nums):
num += 1
print('demo1中的 num:%d'%num)
def demo2(nums):
global num
for i in range(nums):
num += 1
print('demo2中的 num:%d'%num)
def main():
t1 = threading.Thread(target=demo1,args=(100,))
t2 = threading.Thread(target=demo2,args=(100,))
t1.start()
#t1.join()
t2.start()
# demo1中的 num:100
# demo2中的 num:200
# t2.start()
# t1.start()
#demo2中的 num:100
#demo1中的 num:200
if __name__ == '__main__':
main()
#import dis # 可以查看cpu的运行轨迹
#def fn(num):
# num += 1
#print(dis.dis(fn))
6、互斥锁和死锁
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
6.1互斥锁(mutex)
import threading
import time
mutex = threading.RLock() # 创建锁,默认是没有上锁的,可重复的锁可创建多次,但是要注意解锁
# mutex = threading.Lock() # 创建锁,默认是没有上锁的,不可重复的锁只能创建一次
num = 0
def demo1(nums):
global num
mutex.acquire() # 获得互斥锁,锁定上锁
mutex.acquire() # 获得互斥锁,锁定上锁上双锁可以用Rlock但要注意解锁
for i in range(nums):
num += 1
print('demo1中的 num:%d'%num)
mutex.release() # 解锁
mutex.release() # 解锁
def demo2(nums):
global num
mutex.acquire()
for i in range(nums):
num += 1
print('demo2中的 num:%d'%num)
mutex.release()
def main():
t1 = threading.Thread(target=demo1,args=(1000000,))
t2 = threading.Thread(target=demo2,args=(1000000,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
6.2死锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
mutexB.acquire()
print(self.name+'----do1---down----')
# mutexB.release()
mutexA.release()
# 对mutexA解锁
mutexB.release()
# mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
mutexA = threading.Lock()
mutexB = threading.Lock()
if __name__ == '__main__':
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
6.3避免死锁
程序设计时要尽量避免
添加超时时间等
7、Queue线程
from queue import Queue
q = Queue(3) # 初始化Queue(maxsize指定大小):创建一个先进先出的队列
print(q.empty()) # True空的队列 False 队列不是空的 empty():判断队列是否为空
print(q.full()) # False队列不是满的 True队列是满的 full():判断队列是否满了
q.put(1)
q.put(2)
q.put(3) # 多存或者多取都会阻塞
# q.put(4,timeout=2)
# q.put_nowait(4)
print(q.get()) # 多存或者多取都会阻塞
print(q.get())
print(q.get())
# print(q.get(timeout=2))
print(q.get_nowait())
print(q.full())
8、线程同步
'''
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
第一步:mutex解决不了
第二步:用condition解决
'''
# import threading
# import time
#
# class TianMao(threading.Thread):
# def __init__(self,lock):
# self.lock = lock
# super().__init__(name='天猫精灵')
# def run(self):
# # self.lock.acquire()
# print('{}:小爱同学'.format(self.name))
# # self.lock.release()
#
# # self.lock.acquire()
# print('{}:现在几点了'.format(self.name))
# # self.lock.release()
#
# class XiaoAi(threading.Thread):
# def __init__(self, lock):
# super().__init__(name='小爱同学')
# self.lock = lock
#
# def run(self):
# print('{}:在'.format(self.name))
# print('{}:你猜猜现在几点了'.format(self.name))
#
# if __name__ == '__main__':
# mutex = threading.RLock
# t = TianMao(mutex)
# x = XiaoAi(mutex)
# t.start()
# x.start()
'''
第二步condition
'''
import threading
import time
class TianMao(threading.Thread):
def __init__(self, cond):
self.cond = cond
super().__init__(name='天猫精灵')
def run(self):
self.cond.acquire()
print('{}:小爱同学'.format(self.name))
self.cond.notify()
self.cond.wait()
print('{}:现在几点了'.format(self.name))
self.cond.notify()
self.cond.release()
class XiaoAi(threading.Thread):
def __init__(self, cond):
super().__init__(name='小爱同学')
self.cond = cond
def run(self):
# self.cond.acquire()
with self.cond:
self.cond.wait()
print('{}:在'.format(self.name))
self.cond.notify()
self.cond.wait()
print('{}:你猜猜现在几点了'.format(self.name))
# self.cond.notify()
# self.cond.release()
if __name__ == '__main__':
cond = threading.Condition()
t = TianMao(cond)
x = XiaoAi(cond)
x.start()
t.start()
'''谁先等就要谁先运行(调用的顺序是先调用等待的线程),然后通知没等的去执行,有通知就要去找有等待的继续执行'''