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()
下面是使用了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()
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()
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()
控制线程同时执行的数量
使用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 中使用多线程的方法如下:
- 导入
threading
模块。 - 创建一个
Thread
类的实例,并将要执行的函数作为参数传递给该实例。 - 调用
thread.start()
方法启动新线程。 - 调用
thread.join()
方法等待所有线程完成。