【示例-多任务】Python多线程threading模块

Python内置了 threading模块, 是对更底层的thread模块的封装。
内置方法见官方文档: threading - 基于线程的并行

多线程执行

主线程会等待所有的子线程结束后才结束

#coding=utf-8
import threading
import time

def thread_test():
    print("test.")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=thread_test)
        t.start() #启动线程,即让线程开始执行

查看线程数量

#coding=utf-8
import threading
from time import sleep, ctime

def a():
    for i in range(3):
        print("a...%d"%i)
        sleep(1)

def bbbbb():
    for i in range(3):
        print("bbbbb...%d"%i)
        sleep(1)

if __name__ == '__main__':
    print('---开始---:%s'%ctime())
    t1 = threading.Thread(target=a)
    t2 = threading.Thread(target=bbbbb)

    t1.start()
    t2.start()

    while True:
        length = len(threading.enumerate())
        # 下行 返回的结果相同
        # length = threading.active_count()
        print('当前运行的线程数为:%d'%length)
        if length<=1:
            break

        sleep(0.5)

封装threading.Thread 类

继承threading.Thread,重写run方法

#coding=utf-8
import threading
import time

class MyThread(threading.Thread):

    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
            print(msg)


if __name__ == '__main__':
    t = MyThread()
    t.start()

线程执行顺序

每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。

共享全局变量 及 传参

# 共享全局变量
from threading import Thread
import time

g_num = 100

def work1():
    global g_num
    for i in range(3):
        g_num += 1
    print("----in work1, g_num is %d---"%g_num)


def work2():
    global g_num
    print("----in work2, g_num is %d---"%g_num)


print("---线程创建之前g_num is %d---"%g_num)

t1 = Thread(target=work1)
t1.start()

#延时一会,保证t1线程中的事情做完
time.sleep(1)

t2 = Thread(target=work2)
t2.start()
  • 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
  • 缺点: 线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)
from threading import Thread
import time

def work1(nums):
    nums.append(44)
    print("----in work1---",nums)


def work2(nums):
    #延时一会,保证t1线程中的事情做完
    time.sleep(1)
    print("----in work2---",nums)

g_nums = [11,22,33]

t1 = Thread(target=work1, args=(g_nums,))
t1.start()

t2 = Thread(target=work2, args=(g_nums,))
t2.start()

互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

  • 优点 确保了某段关键代码只能由一个线程从头到尾完整地执行
  • 缺点1 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
  • 缺点2 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
import threading
import time

g_num = 0

def test1(num):
    global g_num
    for i in range(num):
        mutex.acquire()  # 上锁
        g_num += 1
        mutex.release()  # 解锁

    print("---test1---g_num=%d"%g_num)

def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()  # 上锁
        g_num += 1
        mutex.release()  # 解锁

    print("---test2---g_num=%d"%g_num)

# 创建一个互斥锁
# 默认是未上锁的状态
mutex = threading.Lock()

# 创建2个线程,让他们各自对g_num加1000000次
p1 = threading.Thread(target=test1, args=(1000000,))
p1.start()

p2 = threading.Thread(target=test2, args=(1000000,))
p2.start()

# 等待计算完成
while len(threading.enumerate()) != 1:
    time.sleep(1)

print("2个线程对同一个全局变量操作之后的最终结果是:%s" % g_num)

互斥锁可以使用上下文管理器
在 with 语句中使用锁、条件和信号量
这个模块提供的带有 acquire() 和 release() 方法的对象,可以被用作 with 语句的上下文管理器。当进入语句块时 acquire() 方法会被调用,退出语句块时 release() 会被调用。因此,以下片段:

with some_lock:
    # do something...

# 相当于:

some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()

现在 Lock 、 RLock 、 Condition 、 Semaphore 和 BoundedSemaphore 对象可以用作 with 语句的上下文管理器。

多线程案例【聊天器】

编写一个有2个线程的程序
线程1用来接收数据然后显示
线程2用来检测键盘数据然后通过udp发送数据

import socket
import threading


def send_msg(udp_socket):
    """获取键盘数据,并将其发送给对方"""
    while True:
        # 1. 从键盘输入数据
        msg = input("\n请输入要发送的数据:")
        # 2. 输入对方的ip地址
        dest_ip = input("\n请输入对方的ip地址:")
        # 3. 输入对方的port
        dest_port = int(input("\n请输入对方的port:"))
        # 4. 发送数据
        udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))


def recv_msg(udp_socket):
    """接收数据并显示"""
    while True:
        # 1. 接收数据
        recv_msg = udp_socket.recvfrom(1024)
        # 2. 解码
        recv_ip = recv_msg[1]
        recv_msg = recv_msg[0].decode("utf-8")
        # 3. 显示接收到的数据
        print(">>>%s:%s" % (str(recv_ip), recv_msg))


def main():
    # 1. 创建套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2. 绑定本地信息
    udp_socket.bind(("", 7890))

    # 3. 创建一个子线程用来接收数据
    t = threading.Thread(target=recv_msg, args=(udp_socket,))
    t.start()
    # 4. 让主线程用来检测键盘数据并且发送
    send_msg(udp_socket)


if __name__ == "__main__":
    main()
发布了76 篇原创文章 · 获赞 221 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/chichu261/article/details/105694956