python 多线程实现多任务 附demo

使用线程完成多任务需要导入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()方法

扫描二维码关注公众号,回复: 3441492 查看本文章
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()

简单认为,哪种行为会在主线程中阻塞主线程的运行,就为哪种行为创建线程去执行该行为,这样不会阻塞主线程的进行。

猜你喜欢

转载自blog.csdn.net/w18306890492/article/details/82872571
今日推荐