一、线程
1.1 多任务:
有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的
1.2 多任务的理解:
- 并行::真的多任务, cpu大于当前执行的任务
- 并发::假的多任务 ,cpu小于当前执行的任务
主线程会等到子线程执行结果之后主线程 才会结束
守护线程 不会等子线程结束 t.setDaemon(True)
等待子线程执行结束 主线程继续执行 t.join()
1.3 查看线程数量
threading.enumerate() 查看当前线程的数量
线程运行是没有先后顺序的,但是先执行主线程再执行子线程
import threading
import time
def demo1():
for i in range(5):
print("--test1--%d" % i)
time.sleep(1)
# break
def demo2():
for i in range(10):
print("--test2--%d" % i)
time.sleep(1)
def main():
t1 = threading.Thread(target=demo1) # 子线程1
t2 = threading.Thread(target=demo2) # 子线程2
t1.start()
# 延时
# time.sleep(1)
t2.start()
# time.sleep(1)
# 获取当前程序所有的线程
# 线程运行是没有先后顺序的
while True:
print(threading.enumerate()) # 主线程
if len(threading.enumerate()) <= 1:
break
time.sleep(1)
if __name__ == '__main__':
main()
1.4 验证子线程的执行与创建
- 当调用Thread的时候,不会创建线程。
- 当调用Thread创建出来的实例对象的start方法的时候,才会创建线程以及开始运行这个线程。
import threading
import time
def demo():
for i in range(5):
print('demo')
def main():
print(threading.enumerate())
# 不会创建线程
t1 = threading.Thread(target=demo)
print(threading.enumerate())
# 创建线程 开始运行
t1.start()
print(threading.enumerate())
if __name__ == '__main__':
main()
1.5 继承Thread类创建线程
import threading
import time
class A(threading.Thread):
# def __init__(self):
# super().__init__()
def run(self):
for i in range(5):
print(i)
def demo(self):
for i in range(5):
print(i)
if __name__ == '__main__':
t = A()
t.start()
t.demo() # 这种方式不是多线程的方式
1.6 多线程共享全局变量(线程间通信)
在一个函数中,对全局变量进行修改的时候,是否要加global要看是否对全局变量的指向进行了修改,
如果修改了指向,那么必须使用global,
仅仅是修改了指向的空间中的数据,此时不用必须使用global
多线程参数-args
threading.Thread(target=test, args=(num,))
import threading
import time
num = [11, 22]
# 多线程共享全局变量的!!!
def demo1(num):
num.append(33)
print("demo1---%s" % str(num))
def demo2(num):
print("demo2---%s" % str(num))
def main():
t1 = threading.Thread(target=demo1, args=(num, ))
t2 = threading.Thread(target=demo2, args=(num, ))
t1.start()
# time.sleep(1)
t2.start()
# time.sleep(1)
print("main---%s" % str(num))
if __name__ == '__main__':
main()
1.7 共享全局变量资源竞争
import threading
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=(10000000,))
t2 = threading.Thread(target=demo2, args=(10000000,))
t1.start()
t2.start()
print('main-----num:%d' % num)
if __name__ == '__main__':
main()
结果:
main-----num :129389
demo2----num :11024590
demo1----num :11115370
字节码:
import dis
def add_sum(a):
a =+ 1
print(dis.dis(add_sum))
# 1 load a
# 2 load 1
# 3 执行add
# 4 赋值给a
1.8 互斥锁:当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
如果在上锁的之前已经上锁了,再上锁会阻塞,等到释放后在上锁
创建锁
mutex = threading.Lock()
锁定
mutex.acquire()
解锁
mutex.release()
import threading
num = 0
# 创建吗一个互斥锁,默认是没有上锁的
mutex = threading.Lock()
def demo1(nums):
global num
# 加锁
mutex.acquire()
for i in range(nums):
num += 1
# 解锁
mutex.release()
print('demo1----num :%d' % num)
def demo2(nums):
global num
# 加锁
mutex.acquire()
for i in range(nums):
num += 1
# 解锁
mutex.release()
print('demo2----num :%d' % num)
def main():
t1 = threading.Thread(target=demo1, args=(10000000,))
t2 = threading.Thread(target=demo2, args=(10000000,))
t1.start()
t2.start()
print('main-----num :%d' % num)
if __name__ == '__main__':
main()
结果:
main-----num :188218
demo1----num :10000000
demo2----num :20000000
1.8 死锁
线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
RLock() 可重入的锁
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解锁
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()
避免死锁
- 程序设计时要尽量避免
- 添加超时时间等
1.9 线程同步
'''
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
'''
import threading
class XiaoAi(threading.Thread):
def __init__(self, cond):
super().__init__(name='小爱同学')
# self.lock = lock
self.cond = cond
def run(self):
with self.cond:
self.cond.wait()
print('{}:在'.format(self.name))
self.cond.notify()
self.cond.wait()
print('{}:你猜呀'.format(self.name))
self.cond.notify()
# self.lock.acquire()
# print("{}:在".format(self.name))
# self.lock.release()
#
# self.lock.acquire()
# print("{}:你猜猜".format(self.name))
# self.lock.release()
class TianMao(threading.Thread):
def __init__(self, cond):
super().__init__(name='天猫精灵')
self.cond = cond
def run(self):
with self.cond:
self.cond.acquire()
print('{}:小爱同学'.format(self.name))
self.cond.notify()
self.cond.wait()
print('{}:现在几点了'.format(self.name))
self.cond.notify()
self.cond.wait()
if __name__ == '__main__':
# mutex = threading.RLock()
cond = threading.Condition()
xiaoai = XiaoAi(cond)
tianmao = TianMao(cond)
# 启动顺序很重要
xiaoai.start()
tianmao.start()
1.10 多任务版的UDP聊天器
'''
多任务版udp聊天:
1 创建套接字
2 绑定本地信息
3 获取对方IP和端口
4 发送、接收数据
5 创建两个线程,去执行功能
'''
import socket
import threading
def recv_msg(udp_socket):
'''发送数据'''
while True:
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
def send_msg(udp_socket, dest_ip, dest_port):
'''接收数据'''
while True:
send_data = input('发送的数据:')
udp_socket.sendto(send_data.encode('gbk'), (dest_ip, dest_port))
def main():
'''完成UDP聊天器'''
# 1 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2 绑定本地信息
udp_socket.bind(('', 7890))
# 3 获取对方IP和端口
dest_ip = '192.168.1.6' # 对方的ip
dest_port = 8080 # 对方的端口
t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
t_send = threading.Thread(target=send_msg, args=(udp_socket,dest_ip, dest_port))
t_recv.start()
t_send.start()
if __name__ == '__main__':
main()