Java 安全发布和初始化安全在.NET中起作用吗?

Java 安全发布和初始化安全在.NET中起作用吗?,java,.net,multithreading,concurrency,Java,.net,Multithreading,Concurrency,我发现《实践中的Java并发性》一书是为Java编写多线程代码的极好指南。我想知道书中描述的一般原则在多大程度上适用于.Net。我对自己的无锁代码不感兴趣——我只想使用可靠、可理解的技术,并利用现有的同步和并发API。从这个意义上讲,这本书给我的主要收获是: 锁定–在一个线程的锁内所做的状态更改对同一对象上的锁内的所有其他线程可见。毫无疑问,这可以在.NET中工作,并且足以编写线程安全程序,但是如果我们不能依赖以下机制,它会导致不必要的锁定。 安全发布–有效不可变对象的最新状态—发布后未更改的对

我发现《实践中的Java并发性》一书是为Java编写多线程代码的极好指南。我想知道书中描述的一般原则在多大程度上适用于.Net。我对自己的无锁代码不感兴趣——我只想使用可靠、可理解的技术,并利用现有的同步和并发API。从这个意义上讲,这本书给我的主要收获是:

锁定–在一个线程的锁内所做的状态更改对同一对象上的锁内的所有其他线程可见。毫无疑问,这可以在.NET中工作,并且足以编写线程安全程序,但是如果我们不能依赖以下机制,它会导致不必要的锁定。 安全发布–有效不可变对象的最新状态—发布后未更改的对象对所有线程可见,前提是满足以下条件之一。请注意,查看对象引用的最新状态不一定与查看对象本身的最新状态相同——在Java安全发布中,这是因为“发生在之前”关系的可传递性。 通过所有线程中的锁同步对其引用的访问 它是通过一个可变变量引用的 它是从同步或并发集合发布的。 正确构造的不可变对象(在.NET中只有最终字段为只读)的最新状态是线程安全的,无论它们是如何发布的。
我希望所有这些工作,否则会使生活变得不必要的困难,但从我所准备的.Net模型来看,至少在指定的情况下是相当脆弱的。有没有人试图为.NET构建一个“先发制人”模型?我认为这是一个急需在.Net中解决的领域。据我所知,没有一本与.Net相当的书可以提供相同的“舒适度”-似乎问题的一部分是缺少一个定义良好的.Net内存模型。

我没有尝试构建一个“发生在之前”的模型,但我在.Net无锁环形缓冲区中实现了无锁数据结构,最近

从volatile变量发布的引用保证是“安全”的,.Net中的volatile关键字强制从内存直接读取该引用,并强制对该引用的任何更改直接写入内存

然而,将两个可变变量相互比较可能会得出陈旧的结论。在我编写的无锁环缓冲区中,这不是问题。当我比较易变的头指针和尾指针时,排队方法偶尔会认为缓冲区已经满了,而当我比较易变的头指针和尾指针时,就会出现一个项目不排队的情况;出于同样的原因,出列方法偶尔会认为缓冲区是空的。然而,在我的例子中,没有数据丢失


因此,为了回答您的一般问题,在.Net中实现无锁数据结构当然是可能的,我已经做到了,而且没有太多的痛苦。

一般概念是相同的。不过,需要考虑Java和.NET内存模型之间的差异。这通常涉及使用、/或中的函数

有两种为.NET指定的内存模型。在.NET Framework ECMA标准第12节分区I中指定的弱内存模型。实际上是由.NET Framework运行时实现的。另一种定义由描述。上描述了一个实际案例,其中.NET内存模型与Java内存模型不同

关于你的观点:

锁定的工作方式与Java中相同

安全发布-前两个场景锁定和工作方式与Java对应场景相同

NET中的同步集合不推荐使用,但它们在内部使用锁,因此它们的工作方式与访问被锁定时完全相同

作为.NET3.5的一部分添加,内部使用无锁技术,因此不同线程之间的内存应该是一致的。但我不确定它们的记忆是否一致


根据定义,不可变对象是线程安全的。一旦你构建了一个不可变的对象,它就不能再被修改了。创建它的线程是唯一可以在缓存中保存其副本的线程。发布后,所有其他线程将获得最新副本,并且该副本保证不会更改。因此,唯一的痛处是出版本身。为了不创建不可变对象的两个实例,您仍然需要关心安全发布,但是当从任何线程访问时,这些实例中的每一个都是线程安全的。
好的,我理解你的担忧,但我看不出有什么具体问题。或者你只是在开始一场关于记忆模型和栅栏的对话?@Tudor-问题是我在标题中写了什么。诚然,这是一个包含多个部分的相当大的问题。我要寻找的是一套简单的规则,这些规则在.net中是安全的——正如我所说的
我不想使用自己的无锁代码——我只想利用现有的线程安全库代码,而不使用不必要的锁。我很乐意接受关于如何改进这个问题的建议——也许我应该把它分解成多个问题?也许最好把它分解。然后,当你有一组较小的问题时,你也可以使用这里的搜索等等。我敢肯定,我以前至少看到过其中一些问题的答案。一篇关于.NET内存模型的有趣文章可从以下网站获得。根据它的说法,.NET内存模型不同于Java,它将对普通变量的写入视为易失性的。如果这是唯一的区别,那么所有Java算法在.NET中都应该可以正常工作。相反的情况未必如此。你知道不可变对象的线程安全是如何工作的吗?根据Bloch的书,不可变对象是线程安全的,不管它们是如何发布的。他在内存模型部分给出了一个惰性初始化的例子,其中对惰性初始化变量的引用不是final、volatile或locked。一旦你构造了一个不可变的对象,它就不能再被修改了。创建它的线程是唯一可以缓存内存的线程。发布后,所有其他线程将获得最新副本,并且该副本保证不会更改。因此,唯一的痛处是出版本身。这又回到了关于安全出版的第2点。你指的是书中的哪个例子?16.4. 由于方法上的synchronized关键字而起作用,例如,它被锁定;16.5. 之所以有效,是因为在Java和.NET中创建第一个实例之前,静态构造函数保证只运行一次;16.6. 是16.5的懒惰版本;16.7可以使用非同步变量发布,因为在最坏的情况下,您将创建同一不可变对象的第二个实例。从任何线程访问都是安全的,但它是不同的副本。他在第16.3节中提到,他指的是可以安全地使用清单16.3,甚至发布到公共字段。在我看来,他是在说,对不可变对象的保证比对有效不可变对象的保证更强,也就是说,对这些对象的引用可以在线程之间共享,而无需同步。不确定比较可变变量是什么意思-你是说它们引用的对象的内容吗始终被视为最新的,但这些变量的值,即它们指向的对象可能不是最新的?