r # 多任务
多任务
多任务介绍
多任务:同时做多个事情(多个任务)
多任务理解
并发:CPU小于当前执行的任务,假多任务。生活中较常见。
当每个程序按个进入CPU执行一次的时候,因为在CPU中运行的时间过短造成了几个程序同时运行的假象就是并发,也就是假多任务。
并行:CPU大于当前执行的任务,真多任务。生活中少见。
当程序各占一个cpu,并不需要时间片轮转就是并行。
线程运行没有先后顺序,先到先得(抢到资源分配)
实现多任务的三种方式
- 线程
- 进程
- 协程
线程
线程介绍
线程(thread)是操作系统能够进行运算调度的最小单位。包含在进程之中,是进程中的实际运行单位。
在Windows的任务管理器中,计算机所运行的程序都是进程
线程是在进程中的。比如QQ。QQ程序是进程,而通过QQ和每个人聊天的聊天窗口就是线程。
使用线程完成多任务
需要使用到 threading模块
threading.Thread(target=xxx):创建线程
target:目标,指定xx函数为子线程【会运行的函数】。注意:输入的函数变量名不需要加括号。
比如需要线程执行demo方法:threading.Thread(target=demo)
启动线程:xx.start()
主线程:当代码运行时候从上至下执行的启动线程称为主线程。
子线程:通过启动的主线程去执行target目标任务的任务内部代码块称为子线程。
注意:只有当子线程全部执行完毕后,主线程才会执行完毕,否则会一直等待执行完毕。
守护线程
当不想要主线程等待子线程全部执行完毕后再完毕时,可以使用
方法一: xx.setDaemon(True)方法:守护线程,不会等待子线程结束
因为启动主线程后面没有其他代码了,所以主线程不再等待,直接结束子线程。
方法二: 在创建线程方法中传入参数daemon=True,如下所示:threading.Thread(target=xx, daemon=True)效果于方法一 setDeamon方法一致。
即 xx.setDaemon(True)等于daemon=True参数
xx.join()
xx.join(): 等待子线程执行结束之后,主线程继续执行。类似将多线程变为但单线程效果。
注意:join方法的位置。不能在主线程之前否则会报错。
xx.run()
xx.run():将线程变为普通的调用方法,而不是线程的多任务同步执行。
注意:run和start的区别。start是线程多任务同步执行,run是线程多任务变为普通调用。
xx.ident()
xx.ident():查看线程的id
注意代码执行 的位置,当该方法在启动线程之前则打印结果为None。因为此时线程还没被创建成功,所以打印结果是空值。
应该是id在创建线程之后打印。即在start后面
xx.is_alive()
xx.is_alive():查看线程是否活跃状态。返回布尔值。是在run方法之前使用。不是在start方式时候使用。
查看线程数量
threading.enumerate():查看当前线程的数量
enumerate():方法经常用于循环遍历中。
比如下图:
列表切片遍历依次获得元素,想要同时获得可用enumerate()方法。传入需要获取的列表名,通过拆包获得想要的数据。
验证子线程的执行与创建
通过查看线程数量方法来判断:
创建线程的另外方式:类的继承
创建一个类继承threading.Thread类
注意要导入模块threading 才能继承。
线程方法的补充
os.getpid()
os模块中getpid():获取当前进程的进程id
如下图所示: 因为主线程和子线程在同一个进程中,所以进程号相同。
active_count()
threading类中的 active_count
active_count():返回当前活跃的线程数。
当导入时间模块,在子线程中进行时间停顿会发现活跃线程数变为2。
原理:当执行子线程数的时候执行到等待1秒钟,也就是子线程代码执行完毕但是需要等待一秒钟才能结束子线程运行。而此时主线程向下执行即打印当前活跃线程。
那么此时子线程在等待一秒钟才结束子线程而主线程也活跃着,所以活跃数是2。
current_thread()
hreading类中的current_thread
current_thread():获取当前线程的xx。
该方法有几个方法能调用。
current_thread().name: 获取当前线程的名字。
多任务版UDP聊天器
import socket
import threading
"""
多任务版UDP聊天
一、创建套接字 SOCK_DGRAM代表UDP
二、创建线程发送内容 sendto
1、发送的内容加上绑定的地址
三、创建线程接收内容 recvfrom
1、需要绑定地址
2、打印接收到的内容
四、关闭套接字
"""
def SendTo(sock_kehu, inp_ipnum):
send_inp = input('需要发送的内容:')
sock_kehu.sendto(send_inp.encode('utf-8'), (inp_ipnum))
def recvFro(sock_kehu):
recv_txt = sock_kehu.recvfrom(1024)
print(recv_txt[0].decode('utf-8'))
def man():
# 设置UDP信息创建
sock_kehu = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
inp_ip = input('请输入IP地址:')
inp_num = int(input('请输入端口号:'))
inp_ipnum = (inp_ip, inp_num)
sock_kehu.bind(inp_ipnum)
# 创建线程t为发送线程 t1为接收线程。通过参数args传参。注意以元组形式传参所以单个参数需要加上逗号
t = threading.Thread(target=SendTo, args=(sock_kehu, inp_ipnum))
t1 = threading.Thread(target=recvFro, args=(sock_kehu,))
# 启动线程
t.start()
t1.start()
# 关闭套接字
sock_kehu.close()
man()
上面代码块需要注意:
线程目标方法如果需要传参需要以元组形式加入参数args。threading.Thread(target=xxx, args=(参数)【元组形式】)
单个参数需要加上逗号代表元组形式
多任务版UDP聊天器循环版
想要不断循环发送接收就需要接收。但是线程的循环并不是无限进行线程循环,而是方法的循环。
即
错误循环一:
错误循环二:
错误循环三:
以上三种循环都会报错
正确循环方式是将子线程执行的目标方法无限循环如下图所示:
但是发现打印效果发送与接收并行了。
所以需要时间等待解决问题。并且时间等待只有在发送前才能达到效果。即在发送数据前加上time.sleep()方法才行。
正确完整版:
import socket
import threading
import time
"""
多任务版UDP聊天
一、创建套接字 SOCK_DGRAM代表UDP
二、创建线程发送内容 sendto
1、发送的内容加上绑定的地址
三、创建线程接收内容 recvfrom
1、需要绑定地址
2、打印接收到的内容
四、关闭套接字
"""
def SendTo(sock_kehu, inp_ipnum):
while True:
time.sleep(1)
send_inp = input('需要发送的内容:')
sock_kehu.sendto(send_inp.encode('utf-8'), (inp_ipnum))
def recvFro(sock_kehu):
while True:
recv_txt = sock_kehu.recvfrom(1024)
print(recv_txt[0].decode('utf-8'))
def man():
# 设置UDP信息创建
sock_kehu = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
inp_ip = input('请输入IP地址:')
inp_num = int(input('请输入端口号:'))
inp_ipnum = (inp_ip, inp_num)
sock_kehu.bind(inp_ipnum)
# 创建线程t为发送线程 t1为接收线程。通过参数args传参。注意以元组形式传参所以单个参数需要加上逗号
t = threading.Thread(target=SendTo, args=(sock_kehu, inp_ipnum))
t1 = threading.Thread(target=recvFro, args=(sock_kehu,))
# 启动线程
t.start()
t1.start()
# 关闭套接字
sock_kehu.close()
man()
多任务版TCP聊天器
可以将一个服务端看成为一个线程,那么一个线程可以服务一个客户端。三个线程就能服务三个客户端。
所以可以在服务端中设置多任务来达到多任务版TCP聊天器。
客户端代码:
import socket
"""
多任务版多任务版TCP聊天
客户端
一、创建套接字 SOCK_STREAMM代表TCP
1、连接服务器
二、创建
1、发送数据
三、
1、接收数据
四、关闭套接字
"""
def SockKehu():
socke_kehu = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 连接服务器
inp_ip = input("输入IP地址:")
inp_addr = int(input