是否Interlocked.Read(ref long)在 64 位架构上进行了“优化”?即如果我正在编写一个可以被两种架构使用的库,我是否应该担心Interlocked.Read在 64 位 CPU 上不必要地使用性能影响?
我想过使用这样的东西,所以我想知道这是否有意义:
// X64 is a preprocessor constant set for x64 builds
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Read(ref long address)
{
#if X64
// atomic on 64-bit processors
return address;
#else
// if I got it right, this creates a full memory barrier
return Interlocked.Read(ref address);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write(ref long address, long value)
{
#if X64
// atomic on 64-bit processors
address = value;
#else
// …Run Code Online (Sandbox Code Playgroud) 我正在编写一个通用类,我需要使用Interlocked.
T test1, test2;
Interlocked.Exchange<T>(ref test1, test2);
Run Code Online (Sandbox Code Playgroud)
这不会编译.所以我被迫使用Exchange(对象,对象)而不是MSDN建议不要那样使用它?
我有一些简单(希望)的问题,我一直无法找到答案 -
假设我有多个线程可以访问的对象a,b.
Interlocked.Exchange(ref a, b)
Run Code Online (Sandbox Code Playgroud)
如果'b'不是易变的,这个操作会不会这样处理?即它会从内存中获取此变量的最新值吗?如果是这样,那写入是'原子'吗?据我所知,Interlocked.Exchange的主要目的是使用新写入将原来的'a'值作为原子操作.但我的主要困惑在于'b'实际写入'a'的价值.
我的第二个问题与本文中的引用有关:
http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/
"一个有趣的观点是,C#中的所有写入都是根据此处和此处记录的内存模型进行的,并且也可能是这样实现的.C#语言的ECMA规范实际上定义了一个较弱的模型,其中写入默认情况下不是易失性的".
这是真的?如果是这样,如果不关心'a'的先前值,是否有Interlocked.Exchange的目的?(与我的第一个例子有关).我没有在StackOverflow上看到任何关于每个写入易失性的文章或评论.但是,我知道写入是原子的.
编辑:如果我的第一个问题的答案是"b"不被视为易失性,而我的第二个问题的答案是写入确实是不稳定的,那么跟进就是,什么时候是联锁的.如果我们不这样做,则有用关心'a'的先前价值?
我正在测试我的计算机架构上的行为Interlocked.Increment和lock行为,因为我阅读了本文中的以下几行。
正如用 Interlocked.Increment 重写的那样,该方法应该执行得更快,至少在某些架构上是这样。
使用以下代码,我确信在我的项目中检查锁是值得的。
var watch = new Stopwatch();
var locker = new object();
int counter = 0;
watch.Start();
for (int i = 0; i < 100000000; i++)
{
lock (locker)
{
counter++;
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds);
watch.Reset();
counter = 0;
watch.Start();
for (int i = 0; i < 100000000; i++)
{
Interlocked.Increment(ref counter);
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds);
Run Code Online (Sandbox Code Playgroud)
我越来越有近似值稳定的结果2.4S用于锁定和1.2秒的互锁。然而,我惊讶地发现在发布模式下运行此代码仅将 Interlocked 的值提高到大约0.7 秒,并且锁定时间保持不变。这是为什么?在释放模式下锁定不是互锁时如何优化互锁?
在MSVC for x64(19.10.25019)上,
InterlockedOr(&g, 1)
Run Code Online (Sandbox Code Playgroud)
生成此代码序列:
prefetchw BYTE PTR ?g@@3JC
mov eax, DWORD PTR ?g@@3JC ; g
npad 3
$LL3@f:
mov ecx, eax
or ecx, 1
lock cmpxchg DWORD PTR ?g@@3JC, ecx ; g
jne SHORT $LL3@f
Run Code Online (Sandbox Code Playgroud)
我本来期望更简单(和无环):
mov eax, 1
lock or [?g@@3JC], eax
Run Code Online (Sandbox Code Playgroud)
InterlockedAnd生成类似的代码InterlockedOr.
必须为此指令设置循环似乎非常低效.为什么会生成此代码?
(作为旁注:我使用的全部原因是InterlockedOr对变量进行原子加载 - 我已经知道这InterlockedCompareExchange是做到这一点的方法.我很奇怪没有InterlockedLoad(&x),但我离题了... )
InterlockedExchange和之间有什么区别InterlockedExchangePointer?
是
if( 0 != InterlockedCompareExchange( ( void** ) &_myVariable
, temp
, 0
) )
Run Code Online (Sandbox Code Playgroud)
和
if( 0 != InterlockedCompareExchangePointer( ( void** ) &_myVariable
, temp
, 0
) )
Run Code Online (Sandbox Code Playgroud)
当量?
我必须将代码移植到VC6,后者不知道这些Interlocked[...]Pointer功能.
编辑:
根据我自己的经验,我知道VC6 非常多,并且没有人会再使用它了.
但是,我不是决策者,对原始问题的回答将受到高度赞赏.
调用后检查溢出的正确方法是什么Interlocked.Increment?
我有一个 ID 生成器,可以在程序执行期间生成唯一的 ID,目前我测试增量是否返回零。
public static class IdGenerator {
private static int _counter = 0;
public static uint GetNewId() {
uint newId = (uint)System.Threading.Interlocked.Increment(ref _counter);
if (newId == 0) {
throw new System.Exception("Whoops, ran out of identifiers");
}
return newId;
}
}
Run Code Online (Sandbox Code Playgroud)
鉴于我每次运行生成的 ID 数量相当大,(在异常大的输入上)_counter增量时可能会溢出,并且我想在这种情况下抛出异常(尽早崩溃以简化调试)。
摘自微软的文档:
location此方法通过包装: if =Int32.MaxValue,location + 1=来处理溢出情况Int32.MinValue。没有抛出异常。
当我读到关于ReaderWriterLockSlim锁机制的时候,有人提出Interlock函数可用于更精细的锁定
...写入对克隆副本进行更改,然后使用Interlocked.CompareExchange交换引用(如果另一个线程在临时中突变了引用,则重新应用它们的更改).
那么,目前,所有我知道的Interlocked对象,是它使用(在多线程环境)做原子additions,compare,compareExchange操作.(我知道如何使用它)
但是(这是我的问题) -
题
我怎样才能将它用作锁?(示例代码将非常感谢)
为简单起见,我正在粘贴此代码(这不是线程安全的 - 如果 Go同时由两个线程调用,则可能会出现一个被零除错误):
class ThreadUnsafe
{
static int _val1 = 1, _val2 = 1;
static void Go()
{
if (_val2 != 0) Console.WriteLine (_val1 / _val2);
_val2 = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用互锁来替换锁(这会解决问题)?
我有一个应用程序,它不断(+-100 毫秒)从 PLC 读取订单,然后将它们放入一个模型中,然后由多个客户端读取。为此,我使用 lock 语句。
订单阅读线程:
lock (model) {
//update object
}
Run Code Online (Sandbox Code Playgroud)
客户阅读:
lock (model) {
//serialize object to json string
}
send over tcp stream to client.
Run Code Online (Sandbox Code Playgroud)
但我也可以用于更新:
Interlocked.ExChange(oldObj, newObj)
Run Code Online (Sandbox Code Playgroud)
我不希望我的客户必须等待订单读取线程中发生的锁定。而且我绝对不希望客户阻止我的订单阅读线程。
我最好使用 Interlocked 吗?
感谢您的建议!
C#的Java等价物是Interlocked.Exchange(Object, Object) : Object什么?Java中是否有一种方法可以在没有锁定的单个原子步骤中执行以下操作:1)在本地存储变量的引用2)设置对同一变量的另一个引用?
interlocked ×10
c# ×8
locking ×4
.net-4.0 ×1
atomic ×1
atomicity ×1
c++ ×1
debug-mode ×1
generics ×1
java ×1
lock-free ×1
release-mode ×1
task ×1
visual-c++ ×1
visual-c++-6 ×1