python3 sleep 并发_Python3标准库之threading进程中管理并发操作方法

本文详细介绍了Python3中的threading模块,包括Thread对象的创建与使用,线程命名,守护线程与非守护线程,枚举所有线程,线程的派生与继承,定时器线程Timer,线程间通信的Event对象,以及Lock、RLock和Condition等同步工具的使用。此外,还讨论了Semaphore限制资源并发访问和threading.local实现线程特定数据。这些内容帮助开发者更好地理解和掌握Python3中的多线程并发管理。
摘要由CSDN通过智能技术生成

1. threading进程中管理并发操作

threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作。

1.1 Thread对象

要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作。

import threading

def worker():

"""thread worker function"""

print('Worker')

threads = []

for i in range(5):

t = threading.Thread(target=worker)

threads.append(t)

t.start()

输出有5行,每一行都是"Worker"。

06e08b62769b04244105cee6b192858a.png

如果能够创建一个线程,并向它传递参数告诉它要完成什么工作,那么这会很有用。任何类型的对象都可以作为参数传递到线程。下面的例子传递了一个数,线程将打印出这个数。

import threading

def worker(num):

"""thread worker function"""

print('Worker: %s' % num)

threads = []

for i in range(5):

t = threading.Thread(target=worker, args=(i,))

threads.append(t)

t.start()

现在这个整数参数会包含在各线程打印的消息中。

bcfb7810b589297f8dfef8f38c760ea3.png

1.2 确定当前线程

使用参数来标识或命名线程很麻烦,也没有必要。每个Thread实例都有一个带有默认值的名,该默认值可以在创建线程时改变。如果服务器进程中有多个服务线程处理不同的操作,那么在这样的服务器进程中,对线程命名就很有用。

import threading

import time

def worker():

print(threading.current_thread().getName(), 'Starting')

time.sleep(0.2)

print(threading.current_thread().getName(), 'Exiting')

def my_service():

print(threading.current_thread().getName(), 'Starting')

time.sleep(0.3)

print(threading.current_thread().getName(), 'Exiting')

t = threading.Thread(name='my_service', target=my_service)

w = threading.Thread(name='worker', target=worker)

w2 = threading.Thread(target=worker) # use default name

w.start()

w2.start()

t.start()

调试输出的每一行中包含有当前线程的名。线程名列中有"Thread-1"的行对应未命名的线程w2。

18c79e8c94cea5369a6ca2099f87be9d.png

大多数程序并不使用print来进行调试。logging模块支持将线程名嵌入到各个日志消息中(使用格式化代码%(threadName)s)。通过把线程名包含在日志消息中,就能跟踪这些消息的来源。

import logging

import threading

import time

def worker():

logging.debug('Starting')

time.sleep(0.2)

logging.debug('Exiting')

def my_service():

logging.debug('Starting')

time.sleep(0.3)

logging.debug('Exiting')

logging.basicConfig(

level=logging.DEBUG,

format='[%(levelname)s] (%(threadName)-10s) %(message)s',

)

t = threading.Thread(name='my_service', target=my_service)

w = threading.Thread(name='worker', target=worker)

w2 = threading.Thread(target=worker) # use default name

w.start()

w2.start()

t.start()

而且logging是线程安全的,所以来自不同线程的消息在输出中会有所区分。

880d7d28dc3db61643dff1823ccf199b.png

1.3 守护与非守护线程

到目前为止,示例程序都在隐式地等待所有线程完成工作之后才退出。不过,程序有时会创建一个线程作为守护线程(daemon),这个线程可以一直运行而不阻塞主程序退出。

如果一个服务不能很容易地中断线程,或者即使让线程工作到一半时中止也不会造成数据损失或破坏(例如,为一个服务监控工具生成“心跳”的线程),那么对于这些服务,使用守护线程就很有用。要标志一个线程为守护线程,构造线程时便要传入daemon=True或者要调用它的setDaemon()方法并提供参数True。默认情况下线程不作为守护线程。

import threading

import time

import logging

def daemon():

logging.debug('Starting')

time.sleep(0.2)

logging.debug('Exiting')

def non_daemon():

logging.debug('Starting')

logging.debug('Exiting')

logging.basicConfig(

level=logging.DEBUG,

format='(%(threadName)-10s) %(message)s',

)

d = threading.Thread(name='daemon', target=daemon, daemon=True)

t = threading.Thread(name='non-daemon', target=non_daemon)

d.start()

t.start()

这个代码的输出中不包含守护线程的“Exiting“消息,因为在从sleep()调用唤醒守护线程之前,所有非守护线程(包括主线程)已经退出。

6df1a8e9ebda846b946a103281fe831c.png

要等待一个守护线程完成工作,需要使用join()方法。

import threading

import time

import logging

def daemon():

logging.debug('Starting')

time.sleep(0.2)

logging.debug('Exiting')

def non_daemon():

logging.debug('Starting')

logging.debug('Exiting')

logging.basicConfig(

level=logging.DEBUG,

format='(%(threadName)-10s) %(message)s',

)

d = threading.Thread(name='daemon', target=daemon, daemon=True)

t = threading.Thread(name='non-daemon', target=non_daemon)

d.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值