gpt4 book ai didi

java - 线程安全和不变的关系

转载 作者:行者123 更新时间:2023-11-30 06:26:04 25 4
gpt4 key购买 nike

通常的观点是,不可变对象是线程安全的。每个有经验的Java开发人员(或任何其他oop开发人员)都知道这一事实,但是当谈到为什么许多开发人员都对我的想法表示怀疑时,我想等等。我认为我就是这些开发人员之一。

线程是有目的的东西。其中之一是改变事物的状态。如果您的线程甚至没有改变,那为什么还要运行这样的线程?

我真的很想看到一个真实的示例,使我说“哦,我必须在这里真正使用不可变的对象来实现线程安全”。

最佳答案

我真的很想看看一个真实的例子,让我说:“哦,我必须
  真正使用不可变对象来实现线程安全”


我从未发现不变性对于多线程至关重要,但另一方面,我在职业生涯中遇到的所有与可变共享状态有关的种族条件和僵局。在事后看来,认识到不变性的好处要比在远见中容易得多。

就是说,很难看到不变性如何解决这些情况下的问题,因为在许多情况下,它不能解决眼前的绘图板问题。有时,您遇到的情况是两个或多个线程需要访问共享状态,而这两个线程都需要查看该共享状态的最新版本。不变性并不能解决任何问题。但是,它可以使错误导致的错误的灾难性降低得多。在某些概念场景(其中有两个或更多)中,您至少可以得到一个更容易使用不可变变量检测/重现的错误,而不是仅在万分之一的用户满月一次的机器上出现一些晦涩的竞赛条件,线程需要以一种共享状态的方式,使它们都能看到共享数据的最新版本。

个人榜样

但是,就像最近的例子一样,我发现不可变的数据结构,或更确切地说是持久的数据结构,对于多线程非常有用:

enter image description here

...是不需要访问最新“变异”的渲染和动画线程。在上面的示例中(它几年前在i3上的原型有点旧,但是我想避免在此站点上展示我的商业作品以避免产生热量),我使用的是我创建的持久性网格数据结构这是不可变的。用户在网格上刷过(超过400万个四边形)时,每一帧都将创建一个新的网格(无法修改原始网格,因为它是不可变的)。但是,新的网格避免了深度复制未修改的数据。


  线程是有目的的东西。其中之一是改变
  状态的东西。如果您的线程甚至没有改变,那为什么呢?
  你会运行这样的线程吗?


我发现不可变网格数据结构立即对多线程有用的地方是渲染和动画线程。两者都不需要查看场景的最新版本。只要提供交互式反馈,它们就可能会落后于用户的更改。他们不需要修改“场景”。他们只需要将某些内容输出到屏幕上,而不需要输出到场景,因此它们的输入是只读的,输出到其他地方,并且如果它们与其他副本/引用/指针没有完美地同步,也可以源数据。

结果,使用这种不可变的网格数据结构,我能够使渲染线程每帧复制整个场景,然后开始渲染它,而其他线程则可以自由创建自己喜欢的新更改网格。如果没有这种不变的网格结构,我可能不得不将渲染线程与进行雕刻和更改网格的线程放在同一线程中,否则,我将不得不认真地对其进行优化以仅复制对象的相关部分。网格(以及整个场景的其余部分)以尽可能快地进行渲染,或者甚至可能做一些细致的工作,并尝试将渲染数据与网格数据同步,并仅在单个线程中选择性地更新其一部分(在锁定),然后渲染线程才能开始工作。

使用不可变的网格数据结构,所有渲染线程要做的就是:

in rendering thread:
on scene change:
copy entire scene // this is dirt cheap
render scene


即使有数百万个多边形,顶点位置,边缘数据和纹理坐标,上面不变的网格的副本占用的空间也不到一千字节(原始占用40兆字节,尽管使用了压缩索引,16位半浮点数等),而在线程不需要保持完美同步(不需要查看最新信息)的情况下,使用大量不变的数据结构获得的超级便宜的复制对于多线程而言确实非常方便。共享数据的最新版本),并且对于撤消系统,非破坏性编辑,实例化,异常安全性等也很方便。

一切都围绕一个不变的数组概念展开,该概念的工作原理如下:

enter image description here

约翰·卡马克

我有点疯了,用C语言构建不可变数据结构,但是自从听到约翰·卡马克(John Carmack)的演讲以来,我一直受到启发,约翰·卡马克(John Carmack)似乎深信您可以创建围绕不可变数据结构和纯函数编程的视频游戏。我曾经认为不变性所涉及的开销(需要创建新的东西并分配内存而不是修改原始内存)会非常巨大,但是如果John Carmack(有点像我的偶像,我们俩都来自同一编程)一代)可以设想一种围绕此类数据结构构建的AAA电子游戏,我想我也应该试一试,因为对他来说足够的东西对我来说应该绰绰有余。 VFX对FPS的要求远不如AAA游戏-艺术家通常会很高兴,如果他们的内容能够获得30+ FPS(尽管内容通常比游戏引擎要处理的内容更为笼统和复杂)。

从那时起,我一直在探索当前领域(电影和电视的视觉特效等)中的这些想法,但我获得的变异结果没有我以前使用的可变数据结构那么快(尽管而不是约翰·卡马克(John Carmack)),但是我可以相当接近地获得奖励,现在,我可以更轻松地创建速度更快的渲染器,动画制作器等(线程可能有点滞后)。它已经如此简化了很多事情(尽管最大的简化实际上并不是多线程,而是非破坏性编辑和实例化)。简化为OMG。我真的不能夸大其词。在某些情况下,它确实将成千上万行代码转换为一行代码*。不变性可能是使您重新思考自己的职业,并想知道为什么不考虑尽快使用它的原因,并且还改变了您对职业生涯中过去的设计决策的反思方式,而当您可能不这样做时,就会犯错误。否则。这是来自一个极度偏见和固执的人,他仍然认为垃圾回收是垃圾。


  公平地讲,实现这些持久数据结构并不是一件容易的事,但是它们花费的时间和所需的代码远远不及减少的时间和代码量。可以说,这样做的好处远远超过了成本,至少在我看来,因为您拥有这种中等复杂的持久性,线程安全的数据结构,作为交换,它极大地简化了系统中一百多个不同的位置,用它。


污垢廉价复制

通常,在这些类型的上下文中,您拥有这些线程,其唯一目的是将某些内容输出到屏幕,并进行大量的中间处理,以将这些像素传送到屏幕,而用户使用的内容可以是其他内容(副本) 。只要这两个以上的副本彼此之间略有不同步,只要帧以用户无法分辨的方式足够快地交付,就可以了。因此,您可以让用户使用副本,而其他线程则在周围复制内容并开始将像素传递到屏幕上。在我的案例中,不变数据结构真正有用的地方在于它们使复制工作变得廉价。如果必须在用户触摸任何东西的每一个帧之间,从线程到线程的所有时间一直复制超过40 MB的数据(在VFX中,数据有时可能跨越GB),则帧速率将开始爬行。使用不变的(特别是持久性的)数据结构,复制变得非常便宜,我们最终甚至只需要复制数千字节的数据,甚至可以复制史诗般的场景。这可能足够快以提供所需的60+ FPS。

作为我最初探索不变性概念的另一件事,我制作了一个粒子演示(也包含400万个粒子:某种程度上我喜欢400万)。在这里,对粒子使用不可变的数据结构并不一定那么有益,但是我只是想要一些非常直观的东西来评估其性能:

enter image description here

而且每一个帧都会创建一个新的粒子集合。我使用不可变的持久数据结构在i3上以超过180 FPS的速度做到了这一点。相同演示的可变版本以超过300+ FPS的速度运行,但是请记住,粒子模拟非常简单(对每个粒子的处理都很简单,如果将更复杂的逻辑应用于每个粒子,差异不会被偏斜) 。我不确定从长远来看,使数据结构不可变地存储粒子是否会带来好处(无法立即想到最有用的案例),但这主要是视觉基准,因为我最初来自80年代和90年代*的游戏开发背景,而我的“可接受的性能”构想则以一种非常视觉化的思维方式与帧速率结合在一起。所以我喜欢以这种方式直观地对事物进行基准测试。为看起来像胡扯的GIF致歉。我很难编码它们,不得不极大地降低它们的帧速率。明显的断断续续/滞后在原始文件中也没有。


  
  实际上,约翰·卡马克(John Carmack)可能会因为
  3D游戏问世,制作成本更高
  这使我在90年代中期失去了独立游戏开发人员的工作,并将我转至VFX。
  电影,电视和档案制作行业,只为游戏创作内容,但无论如何我还是很崇拜他-他似乎总是领先十步
  在过去的几十年中,我在任何时间点上都像我一样,就像一只乌龟试图追赶他,总是落后于他的思想五年左右。现在,他遍历了不变性和功能性编程潮流,我认为他一点都没有变软。我愿意赌博,至少在Gamedev方面,他正在从事真正重要的工作。

关于java - 线程安全和不变的关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47190891/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com