线程安全的类意味着序列化被封装并隐藏在类中,因此我们将有更多机会强制串行执行 - 损失性能。
让线程安全成为客户端的责任会破坏封装(并非总是如此)。根据上下文/设计,线程安全可能非常复杂,或者容易随时间变化(当 API 更改时会破坏您的程序),或者它们根本不统一。抽象同步不一定等同于丢失;它也有可能带来巨大的好处——尤其是因为它不是新手的主题。
最好在更大(或最大)单元 - 应用程序逻辑中管理这些关键部分。
我不确定是谁告诉你的,但这并不一定适用于所有场景。一旦你着手实现并发系统,你就会意识到在你的设计中选择最好的同步粒度会对它的运行方式产生巨大的影响。请注意,“最佳”通用设计对于给定的用途并不总是最好的。
这里没有硬性规定——小而短(然而,可能使用和获取更多数量的锁)对许多设计来说更好,而最大单元会增加争用并导致严重的阻塞。开始更新真的很容易,然后花大量时间在更新中做一些不需要在更新期间持续同步整个结构的事情。在每次访问时锁定整个图并不总是更好,并且结构的某些组件可能是独立于其他组件的线程安全的。因此,最大单元方法通常可能会强制执行影响性能的序列化,尤其是随着大小和复杂性的增长。
那么为什么人们想要线程安全的类呢?他们的真正好处是什么?
我想到了几个很好的理由:
它们可能难以正确实施、诊断和测试。高性能并发设计不是通过参加讲座或通过一些在线教程学到的概念。理解一个好的设计需要很多错误和时间投入。
有些结构非常专业。这些可能是非阻塞的,依赖于原子,或者使用不太典型的并发模式或同步形式。示例:默认情况下,您可能只在需要锁时使用互斥锁,但有时 rwlock 或自旋锁会更好。有时不变性可能会更好。
一些上下文或领域非常专业。设计单个组件通常是一项简单的任务,但设计整个系统以及组件如何交互是一个更大的挑战,并且系统可能需要在特殊约束下运行——依靠该设计的同步可以为您省去很多麻烦。您可能不会花时间在许多不同的工作负载下进行基准测试,而编写它的人已经投入时间来了解实现及其执行。
它只是工作。有些人不想把精力花在并发问题上。他们宁愿使用经过验证的、可靠的实施,并专注于他们计划的其他方面。在某些情况下,您最终使用其软件的人可能对其中一些概念不够了解,当他们选择使用经过验证的(甚至熟悉的)设计时,您会感激不尽。
封装。有时封装会导致并发系统的性能大幅提升。示例:成员或参数可能有条件地不可变,并且可以利用该特征。在其他情况下,封装可能导致较低的采集或减少的阻塞。另一种情况是封装可以降低使用接口的复杂性——可能会删除所有类别的潜在线程问题(尽管可能会留下一组较小的约束)。
比较少理解。重用一个众所周知的实现并理解它是如何运作的,与审查一个手写的实现(例如,你去年离职的同事)相比,你需要学习的东西更少。
当然有缺点,但这不是你的问题;)