- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
因此,我已经对该主题进行了相当长时间的研究,并且我认为我了解最重要的概念,例如释放和获取内存隔离。
但是,对于volatile
和主内存的缓存之间的关系,我还没有找到令人满意的解释。
因此,我了解到每个对volatile
字段的读写都对读取以及在读取之前和之后的写入操作(读取获取和写入释放)执行严格的排序。但这仅能保证操作的顺序。这些更改对其他线程/处理器可见的时间没有说明。特别是,这取决于刷新缓存的时间(如果有的话)。我记得曾经读过Eric Lippert的评论,内容是“ volatile
字段的存在会自动禁用缓存优化”。但是我不确定这到底意味着什么。这是否意味着仅因为我们在某个地方有一个volatile
字段,就完全禁用了整个程序的缓存?如果不是,禁用缓存的粒度是多少?
另外,我读到一些有关强弱可变语义的知识,而C#遵循强语义,即无论是否为volatile
字段,每次写入都将一直直接进入主内存。我对此感到非常困惑。
最佳答案
我先解决最后一个问题。 Microsoft的.NET实现已在writes1上发布了语义。它本身不是C#,所以同一个程序,无论使用哪种语言,在不同的实现中都可能具有较弱的非易失性写入。
副作用的可见性与多个线程有关。不用理会CPU,核心和缓存。相反,想象一下,每个线程都有一个堆上快照的快照,该快照需要某种同步才能在线程之间传递副作用。
那么,C#怎么说呢? C# language specification(newer draft)表示与公共语言基础结构标准(CLI; ECMA-335和ISO/IEC 23271)基本相同,但有所不同。稍后再讨论。
那么,CLI说什么呢?只是不稳定的操作是可见的副作用。
请注意,它还说堆上的非易失性操作也是副作用,但不能保证它是可见的。同样重要的是,它也没有保证它们也不可见。
易失性操作到底会发生什么?易失性读取具有获取语义,它在任何后续的内存引用之前。易失性写具有释放语义,它遵循任何先前的内存引用。
获取锁执行易失性读取,释放锁执行易失性写入。Interlocked
操作具有获取和释放语义。
还有另一个重要的术语要学习,那就是原子性。
对于基本值,在32位体系结构上最多32位,在64位体系结构上最多64位。它们也保证是原子引用。对于其他类型,例如long struct
,这些操作不是原子的,它们可能需要多个独立的内存访问。
但是,即使语义不稳定,读-修改-写操作(例如v += 1
或等效的++v
(或副作用而言,v++
))也不是原子的。
互锁操作可确保某些操作的原子性,通常是加,减和比较和交换(CAS),即当且仅当当前值仍为某个预期值时才写入一些值。 .NET还具有用于64位整数的原子Read(ref long)
方法,该方法甚至在32位体系结构中也可以使用。
我将继续将获取语义称为易失性读取,将语义释放为易失性写入,将其中一个或两个都称为易失性操作。
就订单而言,这一切意味着什么?
在语言级别和机器级别上,易失性读是指没有内存引用可以跨越的点,而易失性写是指没有内存引用可以跨越的点。
如果它们之间没有易失性写入,则该非易失性操作可能会在随后的易失性读取之后与之交互,而如果两者之间没有易失性读取,则该非易失性操作可能会与先前的易失性写入之前相抵触。
线程中的易失性操作是顺序的,不能重新排序。
使线程中的易失性操作对于所有其他线程以相同顺序可见。但是,没有所有线程的易失性操作的总顺序,即,如果一个线程先执行V1然后执行V2,而另一个线程先执行V3然后再执行V4,则任何人都可以观察到在V2之前具有V1且在V4之前具有V3的任何顺序。线。在这种情况下,它可以是以下之一:
V1 V2 V3 V4
V1 V3 V2 V4
V1 V3 V4 V2
V3 V1 V2 V4
V3 V1 V4 V2
V3 V4 V1 V2
即,观察到的副作用的任何可能顺序对于一次执行的任何线程均有效。不需要总排序,因此所有线程只观察一次执行的可能顺序之一。
事物如何同步?
从本质上讲,它可以归结为这一点:同步点是您在易失性写入之后发生易失性读取的位置。
在实践中,您必须检测另一个线程中的易失性写入之后是否在一个线程中发生了易失性读取3。这是一个基本示例:
public class InefficientEvent
{
private volatile bool signalled = false;
public Signal()
{
signalled = true;
}
public InefficientWait()
{
while (!signalled)
{
}
}
}
InefficientWait()
,另一个线程调用
Signal()
,而后者从
Signal()
返回时的副作用对于前者可见。
InefficientWait()
。
var currentValue = Volatile.Read(ref field);
var newValue = GetNewValue(currentValue);
var oldValue = currentValue;
var spinWait = new SpinWait();
while ((currentValue = Interlocked.CompareExchange(ref field, newValue, oldValue)) != oldValue)
{
spinWait.SpinOnce();
newValue = GetNewValue(currentValue);
oldValue = currentValue;
}
SpinLock
,您必须对它进行真正的配置,以针对基于监视器的锁进行配置,因为尽管它们可以使当前线程屈服,但它们不会使当前线程进入休眠状态,类似于所示的
SpinWait
用法。
lock
语句以及线程的创建和终止。
new Thread(...).Start()
在当前线程上释放了语义,并在开始时获取了语义。新线程并退出线程在当前线程上具有释放语义,而
thread.Join()
在等待线程上具有获取语义。
System.Threading
中的类执行,而不是仅通过使用声明为
volatile
的字段并使用
lock
语句来执行。我相信这不是故意的。
object local = field;
if (local != null)
{
// code that reads local
}
if (field != null)
{
// code that replaces reads on local with reads on field
}
NullReferenceException
的成员会抛出
local
。
object local2 = local1;
if (local2 != null)
{
// code that reads local2 on the assumption it's not null
}
if (local1 != null)
{
// code that replaces reads on local2 with reads on local1,
// as long as local1 and local2 have the same value
}
var local = field;
local?.Method()
var local = field;
var _temp = local;
(_temp != null) ? _temp.Method() : null
var local = field;
(local != null) ? local.Method() : null
(field != null) ? field.Method() : null
public class Worker
{
private bool working = false;
private bool stop = false;
public void Start()
{
if (!working)
{
new Thread(Work).Start();
working = true;
}
}
public void Work()
{
while (!stop)
{
// TODO: actual work without volatile operations
}
}
public void Stop()
{
stop = true;
}
}
Stop()
将停止工作程序。 Microsoft的.NET实现保证
stop = true;
是可见的副作用,但不保证
stop
内部对
Work()
的读取不会被忽略:
public void Work()
{
bool localStop = stop;
while (!localStop)
{
// TODO: actual work without volatile operations
}
}
stop
声明为
volatile
。但是还有更多选择,例如使用等效的
Volatile.Read
和
Volatile.Write
,使用
Interlocked.CompareExchange
,在对
lock
的访问周围使用
stop
语句,使用等效于锁的内容(例如
Mutex
) ,或
Semaphore
和
SemaphoreSlim
(如果您不希望该锁具有线程亲和力),即您可以将锁释放到与获取锁的线程不同的线程上,或者使用
ManualResetEvent
或
ManualResetEventSlim
stop
的情况,在这种情况下,您可以使
Work()
超时休眠,同时等待下一次迭代之前的停止信号,等等。
using System;
using System.Threading;
public class SurrealVolatileSynchronizer
{
public volatile bool v1 = false;
public volatile bool v2 = false;
public int state = 0;
public void DoWork1(object b)
{
var barrier = (Barrier)b;
barrier.SignalAndWait();
Thread.Sleep(100);
state = 1;
v1 = true;
}
public void DoWork2(object b)
{
var barrier = (Barrier)b;
barrier.SignalAndWait();
Thread.Sleep(200);
bool currentV2 = v2;
Console.WriteLine("{0}", state);
}
public static void Main(string[] args)
{
var synchronizer = new SurrealVolatileSynchronizer();
var thread1 = new Thread(synchronizer.DoWork1);
var thread2 = new Thread(synchronizer.DoWork2);
var barrier = new Barrier(3);
thread1.Start(barrier);
thread2.Start(barrier);
barrier.SignalAndWait();
thread1.Join();
thread2.Join();
}
}
Thread.Sleep(int)
花费确切的时间。如果是这样,它将正确同步,因为在
DoWork2
执行易失性写入(释放)之后,
DoWork1
将执行易失性读取(获取)。
DoWork2
中,您必须读取与在
DoWork1
中写入的相同易失字段。
关于c# - C# volatile 变量:内存隔离VS。快取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44692780/
这看起来很基础,但我想不通。是否有一种简单的 CSS 唯一方法可以使 cssa 真正快速淡入并缓慢淡出。这是为了改变多个 div 的颜色。大约 0.5 秒的缓入和 2 秒的缓出。 谢谢! 最佳答案 你
我一直在用各种语言和实现实现相同的代码(在 Blackjack 中发牌而不爆牌的方法的数量)。我注意到的一个奇怪之处是,Python 在 C 中调用分区函数的实现实际上比用 C 编写的整个程序快一点。
如果我没看错,/ 意味着它右边的节点必须是左边节点的直接子节点,例如/ul/li 返回 li 项,它们是作为文档根的 ul 项的直接子项。 //ul//li 返回 li 项,它们是文档中某处任何 ul
如何随机更新一个表。所以你给一列一个随机值。并且该列(例如“顶部”)是唯一的。如果您在数字 10 到 20 之间进行选择,并且您有 10 行,那么您就不能有未使用的数字。如果你有 Test table
这在一小部分是一个问题(因为我不明白为什么它会有所不同),在很大程度上是一篇希望能帮助其他一些可怜的程序员的帖子。 我有一个代码库,是我大约 5-7 年前第一次开始 Android 编程时编写的,它具
我正在尝试过滤关系表以获得满足两个条件的表子集(即:我想要 color_ids 为 1 或 2 的条目的所有 ID)。这是一张结实的 table ,所以我正在尝试尽可能多地进行优化。 我想知道是否有人
在上一篇《聊聊PHP中require_once()函数为什么不好用》中给大家介绍了PHP中require_once()为什么不好用的原因,感兴趣的朋友可以去阅读了解一下~ 那么本文将给大家介绍PH
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 10年前关
有没有办法提高glReadPixels的速度?目前我做: Gdx.gl.glReadPixels(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeig
通常,我以函数形式`:=`()来计算data.table中的多列,认为这是最有效的方法。但是我最近发现它比简单地重复使用:=慢。至少在我的电脑上。 我猜想:=的功能形式可能会产生一些开销,但这是它变慢
我的问题是针对 Windows 环境中多线程的性能问题。 在测试我的代码后,我得到的结果是增加线程数不会提高并行计算的性能,并且在经过一些计数后变得更少。到底是怎么回事?是否可以找出最佳线程数的公式:
我看到很少有相同问题的主题,但我仍然无法解决我的问题。这是我的代码 - 使用 XOR 加密的 C 套接字编程 当服务器和客户端连接时:- 用户发送消息,例如:你好- 服务器响应,例如:(服务器):你好
我正在定义继承自 Shape 类并实现“几何”属性的形状。 这是一个例子: public class Landmark : Shape { public override bool IsInB
相同代码在 Android(1Ghz Snapdragon)上的执行速度比我在 3.3 Ghz Core 2 Duo 的 PC(在桌面应用程序中)快 2 倍(PC 的类被复制到 Android 项目)
我需要将一个值与一组数组进行比较。但是,我需要比较 foreach 中的多个值。如果使用 in_array,它可能会很慢,真的很慢。有没有更快的选择?我当前的代码是 foreach($a as $b)
这个问题在这里已经有了答案: How do I write a correct micro-benchmark in Java? (11 个答案) 关闭 9 年前。 今天我做了一个简单的测试来比较
如果比较不应该以这种方式进行,我深表歉意。我是编程新手,只是很好奇为什么会这样。 我有一个包含词嵌入的大型二进制文件 (4.5gb)。每行都有一个单词,后面跟着它的嵌入,它由 300 个浮点值组成。我
我经历了几个不同的四元数乘法实现,但我很惊讶地发现引用实现是迄今为止我最快的实现。这是有问题的实现: inline static quat multiply(const quat& lhs, cons
我写了一个简单的例子,估计调用虚函数的平均时间,使用基类接口(interface)和dynamic_cast和调用非虚函数。这是它: #include #include #include #in
有没有人知道比“StackWalk”更好/更快的获取调用堆栈的方法?我还认为 stackwalk 在有很多变量的方法上也会变慢......(我想知道商业分析员是做什么的?)我在 Windows 上使用
我是一名优秀的程序员,十分优秀!