- 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/
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: How to nest OR statements in JavaScript? 有没有办法做到这一点:
在 JavaScript 中有没有办法让一个变量总是等于一个变量?喜欢var1 = var2但是当var2更新,也是var1 . 例子 var var1 = document.getElementBy
我正在努力理解这代表什么 var1 = var2 == var3 我的猜测是这等同于: if (var2 == var3): var1 = var2 最佳答案 赋值 var1 = var2
这个问题已经有答案了: What does the PHP error message "Notice: Use of undefined constant" mean? (2 个回答) 已关闭 8
我在临时表中有几条记录,我想从每条记录中获取一个值并将其添加到一个变量中,例如 color | caption -------------------------------- re
如何将字符串转为变量(字符串变量--> $variable)? 或者用逗号分隔的变量列表然后转换为实际变量。 我有 2 个文件: 列名文件 行文件 我需要根据字符串匹配行文件中的整行,并根据列名文件命
我有一个我无法解决的基本 php 问题,我也想了解为什么! $upperValueCB = 10; $passNodeMatrixSource = 'CB'; $topValue= '$uppe
这可能吗? php $variable = $variable1 || $variable2? 如果 $variable1 为空则使用 $variable2 是否存在类似的东西? 最佳答案 PHP 5
在 Perl 5.20 中,for 循环似乎能够修改模块作用域的变量,但不能修改父作用域中的词法变量。 #!/usr/bin/env perl use strict; use warnings; ou
为什么这不起作用: var variable; variable = variable.concat(variable2); $('#lunk').append(variable) 我无法弄清楚这一点
根据我的理解,在32位机器上,指针的sizeof是32位(4字节),而在64位机器上,它是8字节。无论它们指向什么数据类型,它们都有固定的大小。我的计算机在 64 位上运行,但是当我打印包含 * 的大
例如: int a = 10; a += 1.5; 这运行得很完美,但是 a = a+1.5; 此作业表示类型不匹配:无法从 double 转换为 int。所以我的问题是:+= 运算符 和= 运算符
您好,我写了这个 MySQL 存储过程,但我一直收到这个语法错误 #1064 - You have an error in your SQL syntax; check the manual that
我试图在我的场景中显示特定的奖牌,这取决于你的高分是基于关卡的目标。 // Get Medal Colour if levelHighscore goalScore { sc
我必须维护相当古老的 Visual C++ 源代码的大型代码库。我发现代码如下: bIsOk = !!m_ptr->isOpen(some Parameters) bIsOk的数据类型是bool,is
我有一个从 MySQL 数据库中提取的动态产品列表。在 list 上有一个立即联系 按钮,我正在使用一个 jquery Modal 脚本,它会弹出一个表单。 我的问题是尝试将产品信息变量传递给该弹出窗
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
jQuery Core Style Guidelines建议两种不同的方法来检查变量是否已定义。 全局变量:typeof variable === "undefined" 局部变量:variable
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: “Variable” Variables in Javascript? 我想肯定有一种方法可以在 JavaScrip
在语句中使用多重赋值有什么优点或缺点吗?在简单的例子中 var1 = var2 = true; 赋值是从右到左的(我相信 C# 中的所有赋值都是如此,而且可能是 Java,尽管我没有检查后者)。但是,
我是一名优秀的程序员,十分优秀!