假设我有一个非易失性的int字段,以及一个它Interlocked.Increment的线程.另一个线程可以直接安全地读取它,还是读取也需要互锁?
我以前认为我必须使用互锁读取来保证我看到当前值,因为毕竟,该字段不是易失性的.我一直在Interlocked.CompareExchange(int, 0, 0)努力实现这一目标.
但是,我偶然发现了这个答案,这表明实际的普通读取总会看到Interlocked.Incremented值的当前版本,并且因为int读取已经是原子的,所以不需要做任何特殊的事情.我还发现了Microsoft拒绝Interlocked.Read(ref int)请求的请求,进一步表明这完全是多余的.
那么我能真正安全地阅读这样一个int领域的最新价值Interlocked吗?
我正在研究VolatileRead/VolatileWrite方法的实现(使用Reflector),我对此感到困惑.
这是VolatileRead的实现:
[MethodImpl(MethodImplOptions.NoInlining)]
public static int VolatileRead(ref int address)
{
int num = address;
MemoryBarrier();
return num;
}
Run Code Online (Sandbox Code Playgroud)
在读取"地址"的值后,如何放置内存屏障?不应该是相反的吗?(在读取值之前放置,所以对于"address"的任何挂起写入都将在我们进行实际读取时完成.同样的事情发生在VolatileWrite,其中内存屏障在赋值之前放置.为什么?另外,为什么这些方法具有NoInlining属性?如果它们被内联会发生什么?
Interlocked.Exchange和Volatile.Write有什么区别?
两种方法都更新某些变量的值.有人可以总结何时使用它们?
http://msdn.microsoft.com/ru-ru/library/bb337971 和http://msdn.microsoft.com/en-us/library/gg712713.aspx
特别是我需要更新我的数组的双项,我希望另一个线程看到最新的值.什么是首选?Interlocked.Exchange(ref arr[3], myValue)或Volatile.Write(ref arr[3], info);在那里arr被声明为double?
================================================== ==========================真实的例子,我声明了这样的双数组:
private double[] _cachedProduct;
Run Code Online (Sandbox Code Playgroud)
在一个线程中,我更新它:
_cachedProduct[instrumentId] = calcValue;
...
are.Set();
Run Code Online (Sandbox Code Playgroud)
在另一个线程中,我像这样读取这个数组:
while(true) {
are.WaitOne();
...
result += _cachedProduct[instrumentId];
...
}
Run Code Online (Sandbox Code Playgroud)
对我来说它只是工作正常.然而,为了确保"它将永远有效",无论它看起来我应该添加Volatile.Write或Interlocked.Exchange.因为双重更新不能保证是原子的http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx
在这个问题的答案中,我希望看到Volatile和Interlocked类的详细比较.为什么我们需要2节课?哪一个和何时使用?
假设我想在线程之间使用布尔状态标志来进行协作取消.(我意识到最好使用一个CancellationTokenSource;这不是这个问题的重点.)
private volatile bool _stopping;
public void Start()
{
var thread = new Thread(() =>
{
while (!_stopping)
{
// Do computation lasting around 10 seconds.
}
});
thread.Start();
}
public void Stop()
{
_stopping = true;
}
Run Code Online (Sandbox Code Playgroud)
问:如果我在另一个线程上调用Start()0s和Stop()3s,那么循环是否保证在当前迭代结束时在10s左右退出?
我见过的绝大多数消息来源表明上述内容应该按预期工作; 见: MSDN ; Jon Skeet ; 布赖恩吉迪恩 ; 马克格拉维尔 ; Remus Rusanu.
但是,volatile只会在读取时生成获取栅栏,并在写入时生成释放栅栏:
易失性读取具有"获取语义"; 也就是说,保证在指令序列之后发生的任何内存引用之前发生.易失性写入具有"释放语义"; 也就是说,保证在指令序列中的写指令之前的任何存储器引用之后发生.(C#规格)
因此,正如Joseph Albahari所观察到的那样,无法保证不会(似乎)交换易失性写入和易失性读取.因此,后台线程可能会在当前迭代结束后继续读取_stopping(即false)的陈旧值.具体地说,如果我Start()在0和Stop()3s …
触发事件时避免竞争条件(在多线程应用程序中)的常见做法是:
EventHandler<EventArgs> temp = SomeEvent;
if (temp != null) temp(e);
"Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible."
Run Code Online (Sandbox Code Playgroud)
问题(根据书中)是"编译器可以优化此代码以完全删除本地临时变量.如果发生这种情况,此版本的代码与第一个版本相同,因此仍然可以使用NullReferenceException"
根据CLR通过C#,这是强制编译器复制事件指针的更好方法.
virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp =
Interlocked.CompareExchange(ref …Run Code Online (Sandbox Code Playgroud) 假设我有一个变量"counter",并且有几个线程通过使用Interlocked访问和设置"counter"的值,即:
int value = Interlocked.Increment(ref counter);
Run Code Online (Sandbox Code Playgroud)
和
int value = Interlocked.Decrement(ref counter);
Run Code Online (Sandbox Code Playgroud)
我可以假设,Interlocked所做的更改将在所有线程中可见吗?
如果没有,我该怎么做才能使所有线程同步变量?
编辑:有人建议我使用volatile.但是当我将"计数器"设置为volatile时,会出现编译器警告"对volatile字段的引用不会被视为volatile".
当我阅读在线帮助时,它说:"通常不应使用ref或out参数传递易失性字段".
以下示例来自MSDN。
public class ThreadSafe
{
// Field totalValue contains a running total that can be updated
// by multiple threads. It must be protected from unsynchronized
// access.
private float totalValue = 0.0F;
// The Total property returns the running total.
public float Total { get { return totalValue; }}
// AddToTotal safely adds a value to the running total.
public float AddToTotal(float addend)
{
float initialValue, computedValue;
do
{
// Save the current running total in a …Run Code Online (Sandbox Code Playgroud) TL;DR:在生产者-消费者队列中,放置一个不必要的(从 C++ 内存模型的角度来看)内存栅栏或不必要的强内存顺序是否有必要以牺牲可能更差的吞吐量为代价来获得更好的延迟?
C++ 内存模型是在硬件上执行的,方法是使用某种内存栅栏来实现更强的内存顺序,而不是将它们放在较弱的内存顺序上。
特别是,如果生产者这样做store(memory_order_release),而消费者使用 观察存储的值load(memory_order_acquire),则加载和存储之间没有围栏。在 x86 上根本没有栅栏,在 ARM 上栅栏是在存储之前和加载之后进行放置操作。
没有围栏存储的值最终会被没有围栏的负载观察到(可能在几次不成功的尝试之后)
我想知道在队列的两侧放置围栏是否可以更快地观察到值?如果有围栏和没有围栏,延迟是多少?
我希望只有一个循环load(memory_order_acquire)和pause/yield限制为数千次迭代是最好的选择,因为它无处不在,但想了解原因。
由于这个问题是关于硬件行为的,我希望没有通用的答案。如果是这样,我主要想知道 x86(x64 风格),其次是 ARM。
例子:
T queue[MAX_SIZE]
std::atomic<std::size_t> shared_producer_index;
void producer()
{
std::size_t private_producer_index = 0;
for(;;)
{
private_producer_index++; // Handling rollover and queue full omitted
/* fill data */;
shared_producer_index.store(
private_producer_index, std::memory_order_release);
// Maybe barrier here or stronger order above?
}
}
void consumer()
{
std::size_t private_consumer_index = 0;
for(;;)
{
std::size_t observed_producer_index = shared_producer_index.load( …Run Code Online (Sandbox Code Playgroud) c# ×7
.net ×2
volatile ×2
.net-4.0 ×1
.net-4.5 ×1
arm ×1
c++ ×1
interlocked ×1
lock-free ×1
memory-model ×1
visibility ×1
x86 ×1