某些语言提供的volatile修饰符被描述为在读取支持变量的内存之前执行"读取内存屏障".
读取存储器屏障通常被描述为一种方法,用于确保CPU在屏障之后执行读取之前执行读取之前所请求的读取.但是,使用此定义,似乎仍然可以读取过时值.换句话说,以特定顺序执行读取似乎并不意味着必须查询主存储器或其他CPU以确保读取的后续值实际上反映了读取屏障时系统中的最新值或随后写入阅读障碍.
因此,volatile是否真的保证读取最新值或者只是(喘气!)读取的值至少与屏障之前的读取一样是最新的?还是其他一些解释?这个答案有什么实际意义?
Nutshell中的C#4(强烈推荐的btw)使用以下代码来演示MemoryBarrier的概念(假设A和B在不同的线程上运行):
class Foo{
int _answer;
bool complete;
void A(){
_answer = 123;
Thread.MemoryBarrier(); // Barrier 1
_complete = true;
Thread.MemoryBarrier(); // Barrier 2
}
void B(){
Thread.MemoryBarrier(); // Barrier 3;
if(_complete){
Thread.MemoryBarrier(); // Barrier 4;
Console.WriteLine(_answer);
}
}
}
Run Code Online (Sandbox Code Playgroud)
他们提到障碍1和4阻止这个例子写0和障碍2和3提供新鲜度保证:他们确保如果B在A之后运行,读_complete将评估为真.
我不是真的得到它.我想我明白为什么壁垒1和4是必要的:我们不想在写_answer进行优化,并放置在写后_complete(屏障1),我们需要确保_answer没有被缓存(光栅4) .我也认为我理解为什么Barrier 3是必要的:如果A在写完_complete = true之后才运行,B仍然需要刷新_complete以读取正确的值.
我不明白为什么我们需要障碍2!我的一部分说这是因为可能线程2(运行B)已经运行直到(但不包括)if(_complete),因此我们需要确保_complete被刷新.
但是,我不知道这有多大帮助.是不是仍然可以在A 中将_complete设置为true但是B方法会看到_complete的缓存(错误)版本?即,如果线程2运行方法B直到第一个MemoryBarrier之后,然后线程1运行方法A直到_complete = true但没有进一步,然后线程1恢复并测试是否(_complete) - 如果不导致错误 …
c# multithreading thread-safety shared-memory memory-barriers
我的问题是关于C#中执行保证的顺序(大概是.Net).我给出了一些我知道要与之比较的Java示例.
对于Java(来自"Java Concurrency in Practice")
无法保证一个线程中的操作将按程序给出的顺序执行,只要在该线程内无法检测到重新排序 - 即使重新排序对其他线程是明显的.
所以代码
y = 10;
x = 5;
a = b + 10;
Run Code Online (Sandbox Code Playgroud)
实际上可以指定a = b + 10在指定y = 10之前
在Java中(来自同一本书)
当线程A启动由同一个锁保护的同步块时,线程A在同步块之前或之前执行的所有内容都对线程B可见.
所以在Java中
y = 10;
synchronized(lockObject) {
x = 5;
}
a = b + 10;
Run Code Online (Sandbox Code Playgroud)
保证y = 10和x = 5都在a = b + 10之前运行(我不知道y = 10是否保证在x = 5之前运行).
C#代码对C#语句的执行顺序有什么保证
y = 10;
lock(lockObject) {
x = 5;
}
a = b + 10;
Run Code Online (Sandbox Code Playgroud)
我特别感兴趣的答案可以提供明确的参考或其他一些非常有意义的理由,因为这样的保证难以测试,因为它们是关于允许编译器做什么的,而不是它每次都做什么以及因为当它们失败时当线程以错误的顺序命中时,你将很难重现间歇性错误.
private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM];
public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info)
{
if (instrument == null || info == null)
{
return;
}
instrumentInfos[instrument.Id] = info; // need to make it visible to other threads!
}
public InstrumentInfo GetInstrumentInfo(Instrument instrument)
{
return instrumentInfos[instrument.Id]; // need to obtain fresh value!
}
Run Code Online (Sandbox Code Playgroud)
SetInstrumentInfo并GetInstrumentInfo从不同的线程调用.
InstrumentInfo是不可变的类.我保证在打电话时有最新的副本GetInstrumentInfo吗?我担心我可以收到"缓存"副本.我应该添加同步吗?
声明instrumentInfos是volatile不会帮助,因为我需要声明数组项为volatile,不是数组本身.
我的代码有问题,如果有问题如何解决?
UPD1:
我需要我的代码在现实生活中工作,不符合所有规范!因此,如果我的代码在现实生活中起作用,但在某些环境下的某些计算机上"理论上"不起作用 - 那没关系!
Thread.MemoryBarrier,除了增加延迟之外什么都不做.我认为我们可以相信微软将在未来版本中继续使用"强大的内存模型".至少微软不太可能改变内存模型.所以我们假设它不会.UPD2: …
好吧,在阅读了Albahari在C#中的线程之后,我试图了解Thread.MemoryBarrier()和乱序处理.
继Brian Gideon对我们为什么需要Thread.MemoerBarrier()的回答后,他提到以下代码会导致程序无限期地在Release模式下循环而且没有连接调试器.
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
// Thread.MemoryBarrier() or Console.WriteLine() fixes issue
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么,在没有添加Thread.MemoryBarrier(),甚至在while循环中的Console.WriteLine()修复问题的原因?
我猜测,因为在多处理器机器上,线程运行自己的值缓存,并且永远不会检索更新的值,stop因为它在缓存中有其值?
或者是主线程没有将它提交给内存?
为什么Console.WriteLine()修复此问题?是因为它还实现了MemoryBarrier吗?
Microsoft的Parallel.For文档包含以下方法:
static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
// A basic matrix multiplication.
// Parallelize the outer loop to partition the source array by rows.
Parallel.For(0, matARows, i =>
{
for (int j = 0; j < matBCols; j++)
{
double temp = 0;
for (int k = 0; k < matACols; k++)
{
temp += matA[i, k] * matB[k, j];
}
result[i, j] = …Run Code Online (Sandbox Code Playgroud) 来自O'Reilly的C#in a Nutshell:
class Foo
{
int _answer;
bool _complete;
void A()
{
_answer = 123;
Thread.MemoryBarrier(); // Barrier 1
_complete = true;
Thread.MemoryBarrier(); // Barrier 2
}
void B()
{
Thread.MemoryBarrier(); // Barrier 3
if (_complete)
{
Thread.MemoryBarrier(); // Barrier 4
Console.WriteLine (_answer);
}
}
}
Run Code Online (Sandbox Code Playgroud)
假设方法A和B在不同的线程上并发运行:
作者说:"障碍1和4阻止这个例子写"0".障碍2和3提供了新鲜度保证:他们确保如果B在A之后运行,则读取_complete将评估为真.
我的问题是: