Python之threading: 带你了解多线程的强大威力!

前言

什么是多线程

多线程是指在一个程序中同时创建和使用多个执行流(thread)来执行不同的任务。这样多个任务就可以同时进行,从而提高程序的执行效率。


在python使用多线程的方法

在 Python 中有两种方法可以使用多线程:使用 Python 自带的 threading 模块,或使用第三方库 multiprocessing

多线程的优势

多线程的好处在于可以利用多核 CPU 的优势,让程序在等待 I/O 操作时使用其他 CPU 核心,从而提高程序的效率。不过由于 Python 的全局解释器锁(Global Interpreter Lock, 简称GIL)的存在,在 Python 中创建的线程并不能真正并行执行。如果你需要在 Python 中创建多个真正并行的线程,可以使用 multiprocessing 库。


线程的安全问题

在使用多线程时,需要注意线程安全问题。如果多个线程同时访问同一个变量,可能会导致数据不一致的问题。为了解决这个问题,可以使用 threading 模块中的锁(Lock)和条件变量(Condition)来控制线程同步

使用锁的方式可以保证线程安全,但是会导致线程的执行效率降低。因此在使用多线程时,需要谨慎考虑是否真的需要使用多线程


threading模块常用方法

threading模块函数 描述
threading.Thread(target=func, args=tuple, kwargs=dict) 使用给定的参数创建一个新的 Thread对象
threading.active_count() 返回当前活动的线程数
threading.current_thread() 返回当前所使用的线程
threading.main_thread() 返回主线程
threading.get_ident() 返回线程标识符
threading.enumerate() 返回当前活动的所有线程的列表
threading.BoundedSemaphore(num) 控制线程同时执行的数量
Thread对象函数 描述
start() 启动线程
join([timeout]) 等待线程结束,如果提供了超时参数,则在超时后退出
is_alive() 判断线程是否运行
setName(name) 设置线程的名称
getName() 获取线程名称

threading模块常用操作

1.创建线程

首先,我们需要创建一个 Thread 类的实例,并将要执行的函数作为参数传递给该实例。例如:

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function)

可以使用 Thread 类的 daemon 属性来设置线程为守护线程。守护线程是一种特殊的线程,它在主线程结束时会自动结束

要将线程设置为守护线程,只需将其 daemon 属性设置为 True

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function, name="MyThread", daemon=True)

2.启动线程

接下来,可以使用 thread.start() 方法来启动新线程,运行 my_function 函数。

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function)
thread.start()

3.等待线程结束

如果要等待所有线程完成,可以使用 thread.join() 方法。例如:

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function)
thread.start()
thread.join()

4.判断线程是否运行

使用 is_alive() 方法可以检查线程是否在运行

在下面例子中,我们调用了线程的 is_alive() 方法来检查它是否在运行。如果线程仍在运行,则会输出 "Thread is still running.",否则会输出 "Thread is not running."

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function)
thread.start()
if thread.is_alive():
    print("Thread is still running.")
else:
    print("Thread is not running.")

5.设置和获取线程名称

可以使用 Thread 类的 setName()getName() 方法来设置和获取线程的名称

import threading

def my_function():
    print("Hello from a new thread!")

thread = threading.Thread(target=my_function)
thread.setName("MyThread")
print(thread.getName())

6.查看当前活动线程数

使用 Python 的内置函数 threading.active_count() 来查看当前活动的线程数

import threading

def my_function():
    print("Hello from a new thread!")

thread1 = threading.Thread(target=my_function)
thread2 = threading.Thread(target=my_function)

thread1.start()
thread2.start()

print(threading.active_count())  #输出2

7.枚举当前活动线程

使用 threading.enumerate() 函数来枚举所有当前活动的线程

import threading

def my_function():
    print("Hello from a new thread!")

thread1 = threading.Thread(target=my_function)
thread2 = threading.Thread(target=my_function)

thread1.start()
thread2.start()

threads = threading.enumerate()
for thread in threads:
    print(thread.getName())

多线程实现共享资源访问

设置线程执行的先后顺序

1.使用join()

首先举个没有使用join()函数的线程例子, 在下述代码的输出可知, 线程的执行是并发的(一起执行的), 并没有明确要求先执行完work1线程后再执行work2线程

import threading
import time
def work1():
    for i in range(4):
        time.sleep(0.5)
        print("work1")

def work2():
    for i in range(4):
        time.sleep(0.5)
        print("work2")

def main():
    thread_add1 = threading.Thread(target=work1)
    thread_add1.start()
    thread_add2 = threading.Thread(target=work2)
    thread_add2.start()
main()

img


下面是使用了join()方法的代码, 从输出结果上看, 可以发现程序先等work1线程执行完后才能执行work2线程

import threading
import time
def work1():
    for i in range(4):
        time.sleep(0.5)
        print("work1")

def work2():
    for i in range(4):
        time.sleep(0.5)
        print("work2")

def main():
    thread_add1 = threading.Thread(target=work1,name="work1") #name参数:给线程命名
    thread_add1.start()
    thread_add1.join()
    thread_add2 = threading.Thread(target=work2,name="work2")
    thread_add2.start()
main()

img


2.使用threading.Lock()

当多个线程试图访问同一个资源时,可能会发生资源竞争。为了避免这种情况,您可以使用 lock() 方法来锁定资源,并在访问完成后解锁

在下面的代码中, 我们使用 lock.acquire() 方法锁定资源,然后使用 lock.release() 方法在访问完成后解锁。这样只有一个线程可以访问资源,其他线程必须等待

import threading
import time
lock = threading.Lock()
def work1():
    lock.acquire()
    for i in range(4):
        time.sleep(0.5)
        print("work1")
    lock.release()

def work2():
    lock.acquire()
    for i in range(4):
        time.sleep(0.5)
        print("work2")
    lock.release()

def main():
    thread_add1 = threading.Thread(target=work1,name="work1") #name参数:给线程命名
    thread_add1.start()
    thread_add2 = threading.Thread(target=work2,name="work2")
    thread_add2.start()


main()

img


3.使用with函数

利用with函数你就可以不写上锁解锁这两行代码了,和文件打开和关闭差不多

import threading
import time
lock = threading.Lock()
def work1():
    with lock:
        for i in range(4):
            time.sleep(0.5)
            print("work1")


def work2():
    with lock:
        for i in range(4):
            time.sleep(0.5)
            print("work2")


def main():
    thread_add1 = threading.Thread(target=work1,name="work1") #name参数:给线程命名
    thread_add1.start()
    thread_add2 = threading.Thread(target=work2,name="work2")
    thread_add2.start()

main()

image-20221217154826309


控制线程同时执行的数量

使用threading.BoundedSemaphore()方法来控制线程同时执行的数量

import threading
import os
import time
semaphore = threading.BoundedSemaphore(5) #设置只能允许5个线程同时进行

def work1(se):
    se.acquire()
    print("test")
    se.release()

def main():
    for i in range(1,15):
        thread = threading.Thread(target=work1,args=(semaphore,)) #这里要注意,args参数需传递一个元组
        thread.start()
main()

注意

  • 使用threading模块的python文件名称不能取"threading", 否则导入threading模块时会报错, 例如: module 'threading' has no attribute '_shutdown'

  • 一旦线程启动,就不能重新启动。如果需要重新启动线程,则必须创建一个新的线程

  • 在使用线程时,需要注意资源竞争。在多线程环境中,各个线程可能会竞争共享资源,因此需要使用适当的同步机制来避免冲突

  • 线程是不能被强制终止的,因此您必须在线程中提供一个退出机制

  • 守护线程在后台运行,不会阻止程序的退出。如果您希望在线程中的任务执行完成后再退出程序,则不应使用守护线程

总结

总结一下,在 Python 中使用多线程的方法如下:

  1. 导入 threading 模块。
  2. 创建一个 Thread 类的实例,并将要执行的函数作为参数传递给该实例。
  3. 调用 thread.start() 方法启动新线程。
  4. 调用 thread.join() 方法等待所有线程完成。
posted @ 2022-12-17 16:20  亨利其实很坏  阅读(304)  评论(0编辑  收藏  举报