Python高级之多任务1(线程)

Python高级之多任务1(线程)

3.1多任务

并行: 任务数小于或者等于cpu的核数就是并行,多个任务真正意义一起执行,提示:只有多核cpu才有并行的操作

并发: 任务数大于cpu的核数,多个任务看起来是一起执行,其实是假象,真正意义上多个任务交替轮流执行

3.2线程

3.2.1创建线程

import threading

sub_thread = threading.Thread(group=None, target=show_info, name=“mythread”, args=(“杨钰莹”, 18))

sub_thread.start()

参数介绍:

(1)group:线程组,注意点:目前group只能使用None,不能修改成其它值。默认group=None

(2)target:执行的目标函数

(3)name: 线程的名字,默认格式Thread-1

(4)args: 以元组方式给目标函数传参

(5)kwargs: 以字典的方式传参

例子:

sub_thread = threading.Thread(target=show_info, kwargs={"name": "陈晓", "age": 28})

sub_thread.start()

 

sub_thread = threading.Thread(target=show_info, args=("陈晓",), kwargs={"age": 28})

sub_thread.start()

3.2.2查看线程

#获取程序中活动线程的列表

提示:只有线程启动,才会放到活动线程的列表里面

thread_list = threading.enumerate()

print(“11–”, thread_list)

#扩展–获取当前活动线程的个数

active_count = threading.active_count()

print(“333–”, thread_list, len(thread_list), active_count)

扩展-获取当前代码执行的线程

current_thread = threading.current_thread()

print(“sing:”, current_thread)

3.2.3线程注意点

(1)线程之间的执行顺序是无序的,是由cpu调度决定的,线程是执行代码的

(2)主线程会等待所有的子线程执行完成以后程序再退出

#不让主线程等待方法实现:

#daemon:表示创建的子线程守护主线程,主线程退出后子线程直接销毁

work_thread = threading.Thread(target=work, daemon=True)

work_thread.start()

work_thread = threading.Thread(target=work)

设置成为守护主线程,主线程退出后子线程直接销毁

work_thread.setDaemon(True)

work_thread.start()

3.2.4自定义线程

import threading

##### 自定义线程类

class MyThread(threading.Thread):

# 提供构造方法def __init__(self, info1, info2):

# 注意点:提供构造方法不会调用父类的构造方法,需要手动自己调用

# 建议:以后如果自己提供了构造方法,自己需要手动调用父类的构造方法super(MyThread, self).__init__()
​        self.info1 = info1
​        self.info2 = info2

# 封装一系列线程相关的任务

def show_info1(self):
    print(self.info1)

def show_info2(self):
    print(self.info2)

# 提示:自定义线程执行任务统一在run方法里面执行

# 注意点:重写父类的run方法

def run(self):
    print("start")
    self.show_info1()
    self.show_info2()

 

# 创建自定义线程对象

# 提示: 自定义线程创建对象的时候不要指定target,因为执行任务都是在run方法里面执行的

my_thread = MyThread("show_info1", "show_info2")

# 注意点:线程启动统一使用start方法,因为start内部调用了run方法

my_thread.start()

3.2.5多线程共享全局变量

import threading
import time

# 全局变量

my_list = list()  #=> [] 元组, 列表, 字典, 集合,range,字符串都是类

# 向全局变量里面添加数据的任务

def add_data():
    # 在此不需要加上global是因为全局变量的内存地址没有发生改变,所以不需要加上global
    for i in range(5):
        my_list.append(i)
        print(id(my_list), i)
        time.sleep(0.1)
    print("add:", my_list)

# 向全局变量里面读取数据

def read_data():
    print("read:", my_list)

if __name__ == '__main__':
    # 创建添加数据的线程
    add_thread = threading.Thread(target=add_data)
    # 创建读取数据的线程
    read_thread = threading.Thread(target=read_data)

# 启动线程

add_thread.start()

# 主线程延时1秒后再继续执行下面的代码

time.sleep(1)
read_thread.start()

3.2.6多线程共享全局变量-问题

(1)如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确

(2)解决方法(两种):

①设置延时

first_thread.start()

time.sleep(1)

second_thread.start()

Or:
②线程等待
主线程等待第一个线程执行完成以后那么代码再继续往下执行

first_thread.start()

first_thread.join() # 线程同步:按照预先定义好的顺序一个任务执行完成另外一个任务再去执行

print(“主线程等待第一个线程执行完成了”)

second_thread.start()

3.2.7互斥锁

互斥锁能保证同一时刻只有一个线程去执行代码,具体哪个线程抢到这个锁我们决定不了,但是能够保证数据的准确性

注意点: 加上互斥锁多任务瞬间变成单任务,执行效率会降低

threading模块中定义了Lock类,可以方便的处理锁定:

创建锁

lock = threading.Lock()

锁定

lock.acquire()

释放

lock.release()

3.2.8死锁

一直等待对方释放锁的情景叫做死锁。要避免死锁的出现,及时释放锁。

3.2.9案例:多任务版udp聊天器

import socket
import threading

# 发送数据的功能函数

def send_msg(udp_socket):

# 接收用户发送的数据

​    send_content = input("请输入您要发送的数据:")

# 对字符串进行gbk进行编码

​    send_data = send_content.encode("gbk")

# 接收用户输入的对方ip

​    dest_ip = input("请输入对方的ip地址:")

# 接收用户输入的对方端口号

​    dest_port = int(input("请输入对方的端口号:"))

# 发送数据

​    udp_socket.sendto(send_data, (dest_ip, dest_port))

# 接收数据的功能函数

def recv_msg(udp_socket):
    while True:
        # 接收数据
        recv_data, ip_port = udp_socket.recvfrom(1024)
        # 对二进制数据进行解码
        recv_content = recv_data.decode("gbk")
        print(recv_content, ip_port)

 

if __name__ == '__main__':

# 创建udpsocket

# 全局变量

​    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
​    udp_socket.bind(("", 9090))

# 创建子线程接收数据

recv_thread = threading.Thread(target=recv_msg, args=(udp_socket,))

# 设置守护主线程,主线程退出子线程直接销毁

recv_thread.setDaemon(True)

# 启动线程

recv_thread.start()

while True:

# 接收用户的指令

​    menu_option = input("请输入功能选项 1:发送 3:退出 >>")if menu_option == "1":

# 发送数据

​        send_msg(udp_socket)elif menu_option == "3":break

# 关闭socket

udp_socket.close()

3.2.10udp发送广播消息

import socket

if __name__ == '__main__':
    # 创建udpsocket
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 设置socket选项, 开启发送广播消息的功能
    # 1. SOL_SOCKET:当前socket
    # 2. SO_BROADCAST: 广播选项
    # 3. True:开启发送广播消息功能
    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
    # 192.168.131.255: 只给131网段发送广播消息
    # 255.255.255.255: 发送广播消息不区分网段
    # 发送广播消息
    udp_socket.sendto("大家好".encode("gbk"), ("255.255.255.255", 9090))
    # 关闭socket
    udp_socket.close()

3.2.11tcp服务端框架

import socket
import threading

# 接收客户端的数据

def recv_msg(ip_port, service_client_socket):
    print(ip_port)
    while True:
        # 接收客户端的消息
        recv_data = service_client_socket.recv(1024)
        # 判断数据是否为空
        if recv_data:
            # 对数据进行解码
            recv_content = recv_data.decode("gbk")
            print(recv_content)
            service_client_socket.send("ok,问题正在处理中...".encode("gbk"))
        else:
            print("客户端发送数据完成,客户端下线了:", ip_port)
            break
    # 关闭服务于客户端的套接字
    service_client_socket.close()

if __name__ == '__main__':
    # 创建tcp服务端socket
    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(("", 8989))
    # 设置监听,把服务端套接字由主动套接字改成被动套接字,被动套接字只能接收客户端的连接请求,不能收发消息
    tcp_server_socket.listen(128)
    while True:
        # 等待接收客户端连接请求
        service_client_socket, ip_port = tcp_server_socket.accept()
        # 创建子线程
        recv_thread = threading.Thread(target=recv_msg, args=(ip_port, service_client_socket))
        # 守护主线程,主线程退出子线程直接销毁
        recv_thread.setDaemon(True)
        # 启动线程执行接收数据的任务
        recv_thread.start()
    # 关闭服务端套接字

​    tcp_server_socket.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值