Python多任务--进程线程协程
b站视频笔记
-----------------------------------------------------------------------------------------------------------------------
一、线程
01 多任务介绍、Thread的基本使用
import time
import threading
def sing():
"""唱歌5秒"""
for i in range(5):
print("----正在唱歌----")
time.sleep(1)
def dance():
"""跳舞5秒"""
for i in range(5):
print("----正在跳舞----")
time.sleep(1)
def main():
t1 = threading.Thread(target=sing) # threading模块的Thread类,传参:函数名
t2 = threading.Thread(target=dance)
t1.start() # 实例对象t1调用start方法,启动线程
t2.start()
if __name__ == "__main__":
main()
import threading
def sing():
"""唱歌5秒"""
for i in range(5):
print("----正在唱歌----")
time.sleep(1)
def dance():
"""跳舞5秒"""
for i in range(5):
print("----正在跳舞----")
time.sleep(1)
def main():
t1 = threading.Thread(target=sing) # threading模块的Thread类,传参:函数名
t2 = threading.Thread(target=dance)
t1.start() # 实例对象t1调用start方法,启动线程
t2.start()
if __name__ == "__main__":
main()
单核CPU:同一时刻只有一件事在做,并发:假的多任务 / 多核:并行:真的多任务,一个萝卜一个坑
操作系统调度方法:时间片轮转;优先级调度
02 Thread创建线程 完成多任务
03 查看正在运行的线程、主线程等待子线程先结束
import threading
import time
def test1():
for i in range(5):
print("-----test1---%d---" % i)
time.sleep(1) # 线程执行顺序不确定,可以用延时控制
def test2():
for i in range(10):
print("-----test2---%d---" % i)
time.sleep(1) # 线程在函数运行完以后结束
def main():
t1 = threading.Thread(target=test1) # 当调用Thread的时候,不会创建线程
t2 = threading.Thread(target=test2)
t1.start() # 当调用Thread创建出来的实例对象的start方法时,才会创建线程以及让线程开始运行
t2.start()
while True:
print(threading.enumerate())
if len(threading.enumerate())<=1:
break
time.sleep(1)
# 子线程结束以后主线程不会结束,相反,主线程挂了,则子线程也结束
if __name__ == "__main__":
main()
import time
def test1():
for i in range(5):
print("-----test1---%d---" % i)
time.sleep(1) # 线程执行顺序不确定,可以用延时控制
def test2():
for i in range(10):
print("-----test2---%d---" % i)
time.sleep(1) # 线程在函数运行完以后结束
def main():
t1 = threading.Thread(target=test1) # 当调用Thread的时候,不会创建线程
t2 = threading.Thread(target=test2)
t1.start() # 当调用Thread创建出来的实例对象的start方法时,才会创建线程以及让线程开始运行
t2.start()
while True:
print(threading.enumerate())
if len(threading.enumerate())<=1:
break
time.sleep(1)
# 子线程结束以后主线程不会结束,相反,主线程挂了,则子线程也结束
if __name__ == "__main__":
main()
04 通过Thread类完成创建线程
import threading
import time
class MyThread(threading.Thread):
def run(self): # start调用一定对应run方法
for i in range(3):
time.sleep(1)
msg = "I'm "+ self.name + '@ '+str(i)
# name属性中保存的时当前线程的名字
print(msg)
self.login() # 线程中想增加其他方法,用self.方法调用
self.register()
def login(self):
print('这是登录')
def register(self):
print('这里注册')
if __name__ == '__main__':
t = MyThread()
t.start()
import time
class MyThread(threading.Thread):
def run(self): # start调用一定对应run方法
for i in range(3):
time.sleep(1)
msg = "I'm "+ self.name + '@ '+str(i)
# name属性中保存的时当前线程的名字
print(msg)
self.login() # 线程中想增加其他方法,用self.方法调用
self.register()
def login(self):
print('这是登录')
def register(self):
print('这里注册')
if __name__ == '__main__':
t = MyThread()
t.start()
运行结果:
>>> I'm Thread-1@ 0
I'm Thread-1@ 1
I'm Thread-1@ 2
I'm Thread-1@ 1
I'm Thread-1@ 2
06 多线程:多线程之间是共享全局变量的
在一个函数中对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局变量的执行指向进行了修改,
如果修改了指向,那么必须使用global,如果仅仅是修改了指向的空间中的数据,此时不用必须使用global
07 args参数
import threading
import time
def test1(temp):
temp.append(33)
print("-----in test1 temp=%s----" % str(temp))
def test2(temp):
print("-----in test2 temp=%s----" % str(temp))
g_nums = [11, 22]
def main():
# target指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候传递什么数据过去
t1 = threading.Thread(target=test1, args=(g_nums,)) # 元组形式
t2 = threading.Thread(target=test2, args=(g_nums,))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print("-----in main Thread g_nums = %s----" % str(g_nums))
if __name__ == "__main__":
main()
import time
def test1(temp):
temp.append(33)
print("-----in test1 temp=%s----" % str(temp))
def test2(temp):
print("-----in test2 temp=%s----" % str(temp))
g_nums = [11, 22]
def main():
# target指定将来这个线程去哪个函数执行代码
# args指定将来调用函数的时候传递什么数据过去
t1 = threading.Thread(target=test1, args=(g_nums,)) # 元组形式
t2 = threading.Thread(target=test2, args=(g_nums,))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print("-----in main Thread g_nums = %s----" % str(g_nums))
if __name__ == "__main__":
main()
08 共享全局变量会出现的问题--资源竞争
错误原因:还没做完,线程就被切换了
解决:原子性,要么不做,要么做完
同步:协同步调,按预定的先后顺序进行运行。
互斥锁:
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
10 死锁 银行家算法
不止一把互斥锁时可能出现,相互等待。
避免死锁:
1、添加超时时间
2、程序设计时要尽量避免(银行家算法)
11 多任务版UDP聊天器(同时收发消息)
补充:
socket套接字(网络通信必备)创建-收发-关闭
创建:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建tcp套接字
s= socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建udp套接字
import socket
import threading
def recv_msg():
"""接收数据并显示"""
# 接收数据
while True:
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
def send_msg():
"""发送数据"""
# 发送数据
while True:
send_data = input("输入要发送的数据:")
udp_socket.sendto(send_data.encode("utf-8"),(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 = input("请输入对方的IP:")
dest_port = int(input("请输入对方的PORT:"))
# 4.创建2个线程,去执行相应的功能
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()
import threading
def recv_msg():
"""接收数据并显示"""
# 接收数据
while True:
recv_data = udp_socket.recvfrom(1024)
print(recv_data)
def send_msg():
"""发送数据"""
# 发送数据
while True:
send_data = input("输入要发送的数据:")
udp_socket.sendto(send_data.encode("utf-8"),(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 = input("请输入对方的IP:")
dest_port = int(input("请输入对方的PORT:"))
# 4.创建2个线程,去执行相应的功能
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()
二、进程
01 进程与程序
程序是死的,运行起来是进程,一个程序可以对应多个进程。进程是资源分配的单位,线程是资源调度的单位。
02 使用Process实现多进程
ps -aux 查看进程,PID是唯一的
import threading
import time
import multiprocessing
def test1():
while True:
print("1---")
time.sleep(1)
def test2():
while True:
print("2---")
time.sleep(1)
def main():
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start() # 创建一个新进程,主进程有什么,子进程就有什么,写时拷贝,否则共享
p2.start()
if __name__ == "__main__":
main()
import time
import multiprocessing
def test1():
while True:
print("1---")
time.sleep(1)
def test2():
while True:
print("2---")
time.sleep(1)
def main():
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start() # 创建一个新进程,主进程有什么,子进程就有什么,写时拷贝,否则共享
p2.start()
if __name__ == "__main__":
main()
06 通过队列Queue获取进程间的数据 (先进先出)
put()放数据,get()取数据
1、数据共享 2、解耦
同一个程序,同一台主机
import multiprocessing
def download_from_web(q):
"""下载数据"""
# 模拟从网上下载数据
data = [11, 22, 33, 44]
# 向队列中写入数据
for temp in data:
q.put(temp)
print("---下载器已经下载完了数据并且存入到队列中----")
def analysis_data(q):
"""数据处理"""
waitting_analysis_data = list()
# 从队列中获取数据
while True:
data = q.get()
waitting_analysis_data.append(data)
if q.empty():
break
# 模拟数据处理
print(waitting_analysis_data)
def main():
# 1.创建一个队列
q = multiprocessing.Queue()
# 2.创建多个进程,将队列的引用当做实参进行传递到里面
p1 = multiprocessing.Process(target=download_from_web, args=(q,))
p2 = multiprocessing.Process(target=analysis_data)
p1.start()
p2.start()
if __name__ == "__main__":
main()
def download_from_web(q):
"""下载数据"""
# 模拟从网上下载数据
data = [11, 22, 33, 44]
# 向队列中写入数据
for temp in data:
q.put(temp)
print("---下载器已经下载完了数据并且存入到队列中----")
def analysis_data(q):
"""数据处理"""
waitting_analysis_data = list()
# 从队列中获取数据
while True:
data = q.get()
waitting_analysis_data.append(data)
if q.empty():
break
# 模拟数据处理
print(waitting_analysis_data)
def main():
# 1.创建一个队列
q = multiprocessing.Queue()
# 2.创建多个进程,将队列的引用当做实参进行传递到里面
p1 = multiprocessing.Process(target=download_from_web, args=(q,))
p2 = multiprocessing.Process(target=analysis_data)
p1.start()
p2.start()
if __name__ == "__main__":
main()
07 进程池 Pool
当需要创建的子进程数量不多时,可以直接用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动创建工作量巨大。
08 多任务文件夹copy
三、协程
利用线程在等待的时间,去处理其他别的事情
#Python#