使用线程完成多任务需要导入threading包:import threading
1.线程类Thread参数说明:
Thread([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前只能使用None
target: 执行的目标任务名
args: 以元组的方式给执行任务传参
kwargs: 以字典方式给执行任务传参
name: 线程名,一般不用设置
2.启动线程
启动线程使用start()方法, sub_thread.start()
3.线程注意点:
线程的执行顺序是无序的
主线程默认会等待所有的子线程结束后才结束
可以设置守护线程,当主线程结束时,子线程立即结束
4.设置守护主线程
# 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码
sub_thread = threading.Thread(target=run, daemon=True)
# 守护主线程方式二
# sub_thread.setDaemon(True)
5.获取当前程序的活动线程列表
thread_list = threading.enumerate()
print(thread_list)
6.自定义线程
import threading
import time
class MyThread(threading.Thread):
def __init__(self,count1, count2):
# 重写父类__init__方法
# 自定义线程类需要继承父类的构造方法
super().__init__()
self.count1 = count1
self.count2 = count2
# 定义成静态方法
@staticmethod
def task1(count):
print("task1",threading.current_thread())
for i in range(count):
print("任务1执行")
time.sleep(0.2)
@staticmethod
def task2(count):
print("task2",threading.current_thread())
for i in range(count):
print("任务2执行")
time.sleep(0.2)
# task1与task2属于同一个线程,顺序执行
def run(self):
self.task1(self.count1)
self.task2(self.count2)
if __name__ == '__main__':
# 创建自定义线程对象,自定义线程指定执行任务统一在run方法里完成,不需要target指定
# my_thread = MyThread()
# my_thread.run()
my_thread = MyThread(5, 5)
print(MyThread.daemon)
# my_thread.setDaemon(True)
my_thread.start()
7.解决多线程共享全局变量的问题
1.使用线程等待,join()方法
import threading
import time
# 全局变量 g_list
g_list = list()
def add_data():
for i in range(10):
g_list.append(i)
print("add", i)
time.sleep(0.2)
print("add_data:", g_list)
def read_data():
print("read_data",g_list)
if __name__ == '__main__':
add_data_thread = threading.Thread(target=add_data)
read_data_thread = threading.Thread(target=read_data)
add_data_thread.start()
# 主线程等待写入线程执行完成以后代码继续往下运行
add_data_thread.join()
read_data_thread.start()
2.互斥锁
import threading
g_num = 0
# 创建锁对象
lock = threading.Lock()
def task1():
# 取到锁 可以执行
lock.acquire()
# g_num为不可变数据 需要声明全局变量数据
global g_num
for i in range(1000000):
g_num += 1
print("task1",g_num)
lock.release()
def task2():
lock.acquire()
global g_num
for i in range(1000000):
g_num += 1
print("task2:",g_num)
lock.release()
if __name__ == '__main__':
first_sub_thread = threading.Thread(target=task1)
second_sub_thread = threading.Thread(target=task2)
first_sub_thread.start()
second_sub_thread.start()
demo1 多任务版 udp聊天器1.0
接受数据会阻塞整个线程 所以创建接收数据的子线程
import socket
import threading
# 显示功能菜单
def show_menu():
# 显示程序的功能菜单
print("-----------udp聊天器1.0------------")
print("0. 退出")
print("1. 发送数据")
print("---------------------------------------")
# 发送数据
def send_msg(current_udp_socket):
# 接收用户输入的数据
send_content = input("请输入您要发送的内容:")
# 对数据进行编码
send_data = send_content.encode("gbk")
# 发送数据
current_udp_socket.sendto(send_data, ("192.134.131.104", 8080))
# 接收数据
def recv_msg(current_udp_socket):
while True:
# 接收数据, 代码执行到recvfrom 会阻塞,直到收到对方的数据以后代码再继续往下执行
recv_data, ip_port = current_udp_socket.recvfrom(1024)
# 对二进制数据进行解码
recv_content = recv_data.decode("gbk")
print("接收到的数据为:", recv_content, ip_port)
if __name__ == '__main__':
# 创建udp的套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 给程序设置端口号
udp_socket.bind(("", 8989))
# 接受数据会阻塞整个线程 所以创建接收数据的子线程
recv_thread = threading.Thread(target=recv_msg, args=(udp_socket,))
# 设置守护主线程
# recv_thread.setDaemon(True)
# 启动子线程,执行对应的任务
recv_thread.start()
while True:
show_menu()
# 接收用户输入的功能选项
menu_option = input("请输入功能选项:")
if menu_option == "1":
# 发送数据
send_msg(udp_socket)
elif menu_option == "0":
break
# 关闭套接字
udp_socket.close()
demo2 多任务版 tcp服务端
当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
import socket
import threading
# 处理客户端的请求操作
def handle_client_request(service_client_socket, ip_port):
# 循环接收客户端发送的数据
while True:
# 接收客户端发送的数据
recv_data = service_client_socket.recv(1024)
if recv_data:
print(recv_data.decode("gbk"), ip_port)
service_client_socket.send("ok,问题正在处理中...".encode("gbk"))
else:
print("客户端下线了:", ip_port)
break
# 终止和客户端进行通信
service_client_socket.close()
if __name__ == '__main__':
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 9090))
tcp_server_socket.listen(128)
# 循环等待接收客户端的连接请求
while True:
# 等待接收客户端的连接请求
service_client_socket, ip_port = tcp_server_socket.accept()
print("客户端连接成功:", ip_port)
# 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
# 设置守护主线程
sub_thread.setDaemon(True)
# 启动子线程
sub_thread.start()
# tcp服务端套接字可以不需要关闭,因为服务端程序需要一直运行
# tcp_server_socket.close()
demo3 多任务版 服务器端文件下载器(异步)
创建子线程,使用子线程处理对应客户端请求操作
import socket
import os
import time
import threading
# 处理客户端下载请求文件的操作
def handle_client_request(ip_port, service_client_socket):
# 代码执行到此,说明连接建立成功
print("客户端的ip地址和端口号:", ip_port)
# 接收客户端请求数据(其实就是客户端发送的文件名)
recv_data = service_client_socket.recv(1024)
# 对二进制数据进行解码
file_name = recv_data.decode("utf-8")
print(file_name)
# 判断指定文件或者文件夹是否存在
if os.path.exists(file_name):
# 根据客户端请求文件名打开指定文件,读取文件中的数据
# with open :
with open(file_name, "rb") as file: # file 表示打开的文件对象
while True:
# 读取文件数据
file_data = file.read(1024)
if file_data:
# 发送数据给客户端
service_client_socket.send(file_data)
# 提示: 发送数据设置延时,测试是否是同时下载文件
time.sleep(0.5)
else:
# 文件读取完成
break
else:
service_client_socket.send("客户端请求的文件不存在!".encode("utf-8"))
# 关闭和客户端通信的套接字
service_client_socket.close()
if __name__ == '__main__':
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 提示: 默认是tcp断开连接,真正释放端口号需要等待1-2分钟
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(("", 8989))
# 设置监听
tcp_server_socket.listen(128)
# 循环等待接收客户端的连接请求,可以服务于多个客户端,不同的客户端可以异步下载文件
while True:
# 等待接收客户端的连接请求
service_client_socket, ip_port = tcp_server_socket.accept()
# 创建子线程,使用子线程处理对应客户端请求操作
sub_thread = threading.Thread(target=handle_client_request, args=(ip_port, service_client_socket))
# 启动子线程执行对应的任务
sub_thread.start()
# 关闭服务端的套接字, 最后这个代码可以省略,因为服务端的程序需要一直运行,服务端的套接字不能关闭
# tcp_server_socket.close()
简单认为,哪种行为会在主线程中阻塞主线程的运行,就为哪种行为创建线程去执行该行为,这样不会阻塞主线程的进行。