使用日志记录是有效的,因为它在内部也使用了锁。这么说
我已经尝试过使用threading.Lock,但是由于必须在不停止运行的情况下执行代码,因此 100 个线程会导致其他线程必须等待直到它们持有锁
是错误的。
这是一个简单的基准,我并不打算让它防弹,只是为了向您展示两种方法的工作方式相似:
import logging
import time
import threading
import random
import sys
logger = logging.getLogger("so70643825")
logger.addHandler(logging.StreamHandler(sys.stdout))
logger.setLevel(logging.INFO)
lock = threading.Lock()
def lock_print(*args, **kwargs):
with lock:
print(*args, **kwargs)
def task_creator(task_number: int, display_function):
def actual_task(x: int):
for i in range(x):
display_function(f"Task {task_number}, {i=}")
time.sleep(random.random() / 10) # sleep up to 0.1s
return actual_task
def run_tasks(number_of_threads: int, number_of_iterations: int, display_function):
threads = []
for thread_number in range(number_of_threads):
thread = threading.Thread(target=task_creator(task_number=thread_number, display_function=display_function),
args=(number_of_iterations,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
def main():
N_THREADS = 100
N_LOOPS = 10
N_MEASURES = 10
times = []
for i in range(N_MEASURES):
time_before_logging_tasks = time.time()
run_tasks(N_THREADS, N_LOOPS, logger.info)
time_after_logging_tasks = time.time()
time_before_locking_tasks = time.time()
run_tasks(N_THREADS, N_LOOPS, lock_print)
time_after_locking_tasks = time.time()
time_logging = time_after_logging_tasks - time_before_logging_tasks
time_locking = time_after_locking_tasks - time_before_locking_tasks
times.append((time_logging, time_locking,))
for time_logging, time_locking in times:
print(f"logging: {time_logging} locking: {time_locking} best={'LOG' if time_logging < time_locking else 'LOCK'}")
if __name__ == "__main__":
main()
结果:
logging: 0.7429051399230957 locking: 0.7113175392150879 best=LOCK
logging: 0.7250735759735107 locking: 0.6937775611877441 best=LOCK
logging: 0.712653636932373 locking: 0.691455602645874 best=LOCK
logging: 0.7124729156494141 locking: 0.6672348976135254 best=LOCK
logging: 0.7507524490356445 locking: 0.7678782939910889 best=LOG
logging: 0.690934419631958 locking: 0.7111172676086426 best=LOG
logging: 0.7271988391876221 locking: 0.7642250061035156 best=LOG
logging: 0.7295012474060059 locking: 0.7794029712677002 best=LOG
logging: 0.7717809677124023 locking: 0.7706160545349121 best=LOCK
logging: 0.7927803993225098 locking: 0.7397556304931641 best=LOCK
LOG 和 LOCK 混合获胜,但每次它们之间的差异很小。
删除随机的sleep 使 LOCK 成为明显的赢家(至少快十倍)。我认为这是因为睡眠使所有日志记录机器引入的开销相形见绌,而 lock_print 不需要。
如果性能对问题非常(我是认真的)很重要,那么你应该在最初就说出来。