2025-03-17 ·
日常编程 ·
244134
单例模式在多线程和多进程环境下的行为分析
单例模式旨在保证一个类仅有一个实例,并提供全局访问点。但在多线程和多进程环境下,其表现却大相径庭。本文将通过代码示例,深入探讨这种差异。
以下代码演示了一个简单的单例模式实现,并分别在多线程和多进程环境下测试:
import multiprocessing
import threading
import time
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class SingletonClass:
count = 0
def __init__(self):
SingletonClass.count += 1
def worker(name):
for _ in range(100):
instance = SingletonClass()
instance.count += 1
time.sleep(0.01)
print(f"{name}: count = {SingletonClass.count}, id = {id(instance)}")
if __name__ == '__main__':
# 多线程测试
threads = [threading.Thread(target=worker, args=(f"Thread-{i}",)) for i in range(2)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
# 多进程测试 (注释掉多线程部分后运行)
# processes = [multiprocessing.Process(target=worker, args=(f"Process-{i}",)) for i in range(2)]
# for process in processes:
# process.start()
# for process in processes:
# process.join()
在多线程环境下运行代码,你会发现两个线程访问的是同一个SingletonClass实例,id(instance)值相同,count值会正确递增。这是因为多线程共享进程的内存空间。
然而,如果取消多线程部分的注释,运行多进程部分,你会观察到每个进程都创建了自己的SingletonClass实例,id(instance)值不同,count值在每个进程内独立递增。这是因为每个进程拥有独立的内存空间,单例模式的instances字典在每个进程中都是独立存在的。
这种差异的根本原因在于进程间内存隔离。多线程共享内存,因此单例模式的全局状态得以保持;而多进程拥有独立的内存空间,单例模式的全局性在进程间失效。 因此,在多进程环境下,需要采用其他机制(例如,使用Manager或其他进程间通信方式)来实现真正的单例模式。 上述代码的单例模式实现仅在多线程环境下有效。
以上就是单例模式在多线程和多进程环境下为何表现不同?的详细内容,更多请关注骃骐网【www.myinqi.com】。
单纯用zipfile.ZipFile.extractall()解大型ZIP文件速度慢,因其本质是单线程顺序处理,不支持并发;有效提速方式是用ThreadPoolExecutor并发调用extract(),每个线程独立打开ZIP文件处理单个条目,并注意编码兼容、内存控制与线程安全。 单纯用 zipfile.ZipFile.extractall() 解大型 ZIP 文件,基本就是单核硬扛,速度瓶颈明显...
2026-05-29 · 日常编程 · 132812
RabbitMQ本身保证单条消息只投递一次给一个消费者,数据一致性问题源于业务逻辑并发执行导致的竞态,而非消息重复投递;basic_ack仅确认消息接收,不保障业务执行唯一性,故无法解决数据一致性问题。 多进程消费同一个RabbitMQ队列时,数据一致性问题不来自RabbitMQ本身(它天然保证单条消息只投递一次给一个消费者),而来自业务逻辑并发执行导致的竞态——比如多个进程同时读写同一张数据库表...
2026-05-29 · 日常编程 · 220341
ProcessPoolExecutor比手写Process更稳,因其内置进程复用、超时控制、异常透传;避免AttributeError需将函数置于模块顶层且非lambda/嵌套;max_workers应据内存与任务粒度调优,而非盲目设为cpu_count()。 训练模型时 CPU 占用率“过高”不是 bug,而是 multiprocessing 正常工作的表现——它真正在压满多核,而不是像 thr...
2026-05-29 · 日常编程 · 55765
pytest测多线程安全会“测不准”是因为其单线程执行机制导致测试函数返回即判定完成,后台线程可能未结束;必须用join显式同步、Event/Lock制造竞态、捕获线程异常并共享被测对象。 为什么直接用 pytest 测多线程安全会“测不准” 因为 pytest 默认是单线程执行测试函数,即使你在测试里手动启动多个 threading.Thread,它也不会自动等待所有线程结束就判定测试通过——t...
2026-05-29 · 日常编程 · 283902
subprocess.check_output 默认吞掉 stderr 且不暴露,仅当退出码非零才抛异常;需改用 subprocess.run(..., capture_output=True, check=True) 分别获取 stdout 和 stderr。 subprocess.check_output 会吞掉 stderr 吗 会,而且默认情况下完全不暴露 stderr。调用 subpro...
2026-05-29 · 日常编程 · 111963
Django 的 send_mail 是同步阻塞调用,会导致视图卡住、HTTP 超时、worker 占满等问题;应改用 threading(轻量)或 Celery+Redis(生产),并优先使用 EmailMessage 支持 HTML、附件等功能。 同步 send_mail 会卡住视图,别直接用 Django 的 send_mail 是纯同步阻塞调用,SMTP 连接、TLS 协商、认证、发信全程...
2026-05-26 · 日常编程 · 233507
不能直接在async函数里用openpyxl或pandas.to_excel,因为二者均为同步阻塞调用,会卡住event loop;即便用await asyncio.to_thread()包装,Excel写入本质是CPU密集型操作,受GIL限制,线程无法真正并行,反而降低性能;必须改用multiprocessing+asyncio.run_in_executor组合,将Excel构建移至独立子进程...
2026-05-25 · 日常编程 · 91212
准确判断DDP中rank 0主进程需先检查torch.distributed.is_initialized()为True,再调用torch.distributed.get_rank()==0;单卡调试时可默认rank 0,但生产环境必须显式初始化且在模型封装前完成。 如何准确判断 DDP 中的 rank 0 主进程 DDP(DistributedDataParallel)下,torch.distr...
2026-05-24 · 日常编程 · 195063
pyodbc 支持多进程和多线程并行数据库操作,但连接必须在各进程/线程内独立创建;直接复用连接会导致错误。实际性能瓶颈常在 i/o 而非 cpu,因此线程池通常比进程池更高效、资源更轻量。 pyodbc 支持多进程和多线程并行数据库操作,但连接必须在各进程/线程内独立创建;直接复用连接会导致错误。实际性能瓶颈常在 i/o 而非 cpu,因此线程池通常比进程池更高效、资源更轻量。 在 Python...
2026-05-24 · 日常编程 · 19741
本文探讨在 Laravel 5.8 环境下,如何通过重启队列工作进程来管理长时间运行或异常的队列任务,特别是当面临难以直接删除的超长延迟任务时。核心策略是利用 php artisan queue:work --stop-when-empty 命令实现工作进程的优雅关闭和重启,确保队列系统健康运行。理解 Laravel 队列中的任务管理挑战 在 Laravel 队列系统中,特别是当任务被配置了极长的...
2025-08-28 · 日常编程 · 104642