- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我目前正在研究写时复制集的实现,并想确认它是线程安全的。我相当确定唯一可能不是这样的方法是允许编译器在某些方法中重新排序语句。例如,Remove
方法如下所示:
public bool Remove(T item)
{
var newHashSet = new HashSet<T>(hashSet);
var removed = newHashSet.Remove(item);
hashSet = newHashSet;
return removed;
}
其中hashSet定义为
private volatile HashSet<T> hashSet;
所以我的问题是,假设 hashSet 是 volatile
,这是否意味着新集合上的 Remove
发生在写入成员变量之前?如果不是,则其他线程可能会在删除发生之前看到该集合。
我实际上在生产中没有看到任何问题,但我只是想确认它是安全的。
更新
更具体地说,还有另一种获取IEnumerator
的方法:
public IEnumerator<T> GetEnumerator()
{
return hashSet.GetEnumerator();
}
所以更具体的问题是:是否可以保证返回的 IEnumerator
永远不会从删除中抛出 ConcurrentModificationException
?
更新 2
抱歉,答案都是解决多个作者的线程安全问题。提出了好的观点,但这不是我想在这里找到的。我想知道是否允许编译器将 Remove
中的操作重新排序为如下所示:
var newHashSet = new HashSet<T>(hashSet);
hashSet = newHashSet; // swapped
var removed = newHashSet.Remove(item); // swapped
return removed;
如果这是可能的,则意味着线程可以在 hashSet
分配之后但在 item 之前调用
被移除,这可能导致集合在枚举期间被修改。GetEnumerator
Joe Duffy 有一个 blog article指出:
Volatile on loads means ACQUIRE, no more, no less. (There are additional compiler optimization restrictions, of course, like not allowing hoisting outside of loops, but let’s focus on the MM aspects for now.) The standard definition of ACQUIRE is that subsequent memory operations may not move before the ACQUIRE instruction; e.g. given { ld.acq X, ld Y }, the ld Y cannot occur before ld.acq X. However, previous memory operations can certainly move after it; e.g. given { ld X, ld.acq Y }, the ld.acq Y can indeed occur before the ld X. The only processor Microsoft .NET code currently runs on for which this actually occurs is IA64, but this is a notable area where CLR’s MM is weaker than most machines. Next, all stores on .NET are RELEASE (regardless of volatile, i.e. volatile is a no-op in terms of jitted code). The standard definition of RELEASE is that previous memory operations may not move after a RELEASE operation; e.g. given { st X, st.rel Y }, the st.rel Y cannot occur before st X. However, subsequent memory operations can indeed move before it; e.g. given { st.rel X, ld Y }, the ld Y can move before st.rel X.
我读这篇文章的方式是调用 newHashSet.Remove
需要一个 ld newHashSet
并且写入 hashSet
需要一个 st.rel newHashSet
。从上面的 RELEASE 定义来看,在存储 RELEASE 之后没有负载可以移动,所以语句不能重新排序!有人可以确认我的解释是正确的吗?
最佳答案
考虑使用 Interlocked.Exchange - 它将保证订购,或 Interlocked.CompareExchange作为附带好处,您可以检测(并可能从中恢复)对集合的同时写入。显然,它增加了一些额外的同步级别,因此它与您当前的代码不同,但更容易推理。
public bool Remove(T item)
{
var old = hashSet;
var newHashSet = new HashSet<T>(old);
var removed = newHashSet.Remove(item);
var current = Interlocked.CompareExchange(ref hashSet, newHashSet, old);
if (current != old)
{
// failed... throw or retry...
}
return removed;
}
而且我认为在这种情况下您仍然需要 volatile hashSet
。
关于c# - 重新排序围绕 volatile 的操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11357439/
这是一种复杂的情况。我正在重构(从头开始)c++,它必须用作 CGI 脚本和独立应用程序的核心。 遗憾的是,我从大学开始就没有写过C++,对c#/Java比较熟悉。所以我打算将 WPF 用于 GUI。
您好,我正在尝试找出与此线程中提出的问题相同的问题 How to use CSS to surround a number with a circle? 但是 - 每次我这样做时,形状都会变成椭圆形,
如果您在单个语句中执行某些操作,例如“abc”+ stringval +“abc”,那么是一个不可变的字符串副本,还是两个(注意 abc 和 123 在编译时是常量) 奖励回合:使用像下面这样的 St
我正在尝试创建一个查询,该查询只会在满足某些条件的情况下添加 AND 子句。 这就是我所追求的: SELECT DISTINCT id name active FROM team WHER
在使用 Google 的出色绘图工具进行了一些试验后,我正在使用 Gnuplot 绘制几个 3D 图形。我喜欢 Google 工具的一件事是它在表面周围绘制的“边界框”,这让我更容易看到大小。 有没有
我们最近从solr迁移到 Elasticsearch 。 因此决定以自定义查询格式编写一个包装器,该包装器将转换为 Elasticsearch 查询。将来,如果我们更改为另一个数据存储,则只需要修改此
我有以下内容将音频剪辑的频率绘制为条形音箱: const drawSinewave = function() { requestAnimationFrame(drawSinewave);
我试图围绕其父矩形的中心旋转一个矩形。 child 到 parent 边界的距离必须始终保持不变。我几乎成功了,但我的方法似乎有一个小错误。我似乎找不到问题所在。 示例: http://jsfiddl
我有一个帮助类来将用户对象保存到共享首选项。我用过 serialize(): String函数和 create(serializedString: String)我的 User 中的函数数据模型。他们
是否可以围绕 UIBezierPath 的可见部分绘制路径? 这是我的问题的一个例子 这是我想要完成的 这是我到目前为止得到的: - (void)drawRect:(CGRect)rect { C
这里,AsciiChecker启用文本形式的矩阵规范。 abstract class AsciiChecker extends AlgoritmicChecker { String[] asc
目前,我有十个不同的查询,它们通过 JDBC 处理,并包装在返回 ResultSet 的函数中。这些 ResultSet 对象中的每一个都由外部程序进行迭代,并将通过其索引而不是根据要求的列名进行访问
围绕 finder 方法启动事务是否明智: @Transactional public E getParticularEvent(final String id) { return (E)em
我需要一个围绕 Canvas 边缘移动的圆圈。向右然后向下移动可以正常工作,但是当它需要向左移动时,它会跳到右下角并开始一次又一次地向右移动。我不知道如何解决这个问题。 var can = doc
我正在尝试我的第一个 jQuery 插件。 (耶……时间到了!) 我很难思考如何让一个可公开访问的函数正常启动。 代码 (function($, doc, win){ "use strict"
在阅读了很多关于绕相机旋转的指南并询问了一些关于 SO 的其他问题后,我想到了 SSCCE我到目前为止所拥有的。也许这样其他人会更容易理解我需要什么,对我来说答案是什么。 到目前为止它看起来像这样:
这里是 Java 菜鸟!我正在努力为我正在编写的 Android 应用程序画龙点睛。本质上,它是一个 RSS 阅读器。异步任务获取 RSS 提要。然后对其进行解析,我想做的最后一点是使用已解析的 RS
我有以下代码,从数据库的“类(class)”表中选择标题和图像。 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
我正在尝试实现一个表盘,其中一只手的位图图像围绕 Canvas 上的表盘中心旋转。 基本上在 onDraw() 方法中,我希望能够将图像资源放到 Canvas 上,然后每秒旋转一次。 我有每秒触发一次
我从 SwingX 找到了一个名为 JXLoginPane 的组件在 WindowBuilder 中可用,这似乎是我尝试做的事情的一个很好的起点,但我需要有关如何使用它的更多信息。到目前为止,我发现唯
我是一名优秀的程序员,十分优秀!