我正在尝试使用Interlocked.CompareExchange这个枚举:
public enum State {
Idle,
Running,
//...
}
Run Code Online (Sandbox Code Playgroud)
以下代码无法编译,但这就是我想要做的:
if (Interlocked.CompareExchange(ref state, State.Running, State.Idle) != State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
Run Code Online (Sandbox Code Playgroud)
当然我可以使用int而不是枚举并使用属性:
private int state = (int)State.Idle;
public State { get { return (State)state; } }
Run Code Online (Sandbox Code Playgroud)
然后将枚举转换为int:
if (Interlocked.CompareExchange(ref state, (int)State.Running, (int)State.Idle) != (int)State.Idle) {
throw new InvalidOperationException("Unable to run - not idle");
}
Run Code Online (Sandbox Code Playgroud)
但有没有更好的方法来做到这一点?
我一直在使用C#,并且最近开始致力于为我的侧面项目添加并行性.因此,根据微软的说法,读取和写入int甚至浮点数都是原子的
我确信这些原子性要求在x86架构上运行得很好.但是,在ARM(可能没有硬件浮点支持)等体系结构上,似乎这些保证很难.
问题只是因为'int'总是32位而变得更加重要.有许多嵌入式设备无法自动执行32位写入.
这似乎是C#中的一个根本错误.保证这些数据类型的原子性不能轻松完成.
这些原子性保证如何在没有FPU或32位写入的架构上实现?
如何在Java中原子地和线程安全地修改int?
原子增量,测试和设置等......?
简化问题:
与内存屏障相比,互锁操作引起的内存缓存一致性(或"刷新")的时间是否存在差异?让我们在C#中考虑 - 任何互锁操作与Thread.MemoryBarrier().我相信存在差异.
背景:
我读了很少关于内存障碍的信息 - 所有对预防特定类型的内存交互指令重新排序的影响,但是我找不到关于它们是否应该立即刷新读/写队列的一致信息.
实际上我发现很少有消息来源提到不能保证操作的即时性(只保证特定的重新排序是有保证的).例如
维基百科:"但是,要明确的是,这并不意味着任何操作都会在屏障完成时完成;只有完成操作的订购(当它们完成时)才能得到保证"
Freebsd.org(障碍是硬件特定的,所以我猜一个特定的操作系统并不重要):"内存屏障只是确定内存操作的相对顺序;它们不保证内存操作的时间"
另一方面,Interlocked操作 - 从他们的定义 - 导致立即刷新所有内存缓冲区以保证更新的最新值更新导致内存子系统用值锁定整个缓存行,以防止从任何访问(包括读取)其他CPU /核心,直到操作完成.
我纠正还是错了?
免责声明:
这是我在这里的原始问题的演变.在.NET中的可变新鲜度保证(易失性与易失性读取)
EDIT1: 修复了关于Interlocked操作的声明 - 内联文本.
编辑2: 完全删除演示代码+它的讨论(因为一些人抱怨太多的信息)
System.Threading.Interlocked.CompareExchange operator提供了Compare-And-Swap操作的原子(因此是线程安全的)C#实现.
例如,int i = 5; Interlocked.CompareExchange(ref i, 10, 5);在此命令之后,int i将具有值= 10.并且比较和交换以原子方式发生(单个操作).
当我尝试将其与类实例一起使用时,比较失败并且不交换值.
public class X
{
public int y;
public X(int val) { y = val; }
}
Run Code Online (Sandbox Code Playgroud)
现在当我这样做
X a = new X(1);
X b = new X(1);
X c = new X(2);
Interlocked.CompareExchange<X>(ref a, c, b);
Run Code Online (Sandbox Code Playgroud)
比较和Exchange操作失败.所以,我将X类的Equals和==运算符重写为
public override bool Equals(object obj) { return y == ((X) obj).y; }
Run Code Online (Sandbox Code Playgroud)
所以,现在我得到Interlocked.Equals(a,b)了true,但CompareExchange操作仍然失败.
有没有办法做到这一点?我想比较两个类实例,并根据比较为其中一个指定一个值.
我只是想知道这个代码是否是一个开发人员(后来已经离开)是好的,我想他想避免锁定.这与仅仅使用直接锁之间有性能差异吗?
private long m_LayoutSuspended = 0;
public void SuspendLayout()
{
Interlocked.Exchange(ref m_LayoutSuspended, 1);
}
public void ResumeLayout()
{
Interlocked.Exchange(ref m_LayoutSuspended, 0);
}
public bool IsLayoutSuspended
{
get { return Interlocked.Read(ref m_LayoutSuspended) != 1; }
}
Run Code Online (Sandbox Code Playgroud)
我认为锁定这样的东西会更容易吗?它确实会被多个线程使用,因此决定使用锁定/互锁的原因.
我有一个迁移工作,我需要在完成后验证目标数据.为了通知管理员验证成功/失败,我使用一个计数器来比较Database1中表Foo的行数和Database2中表Foo的行数.
Database2中的每一行都根据Database1中的相应行进行验证.为了加快这个过程,我使用了一个Parallel.ForEach循环.
我最初的问题是计数总是与我的预期不同.我后来发现+=和-=操作不是线程安全的(不是原子的).为了解决这个问题,我更新了用于Interlocked.Increment计数器变量的代码.此代码打印的计数更接近实际计数,但是,每次执行时它似乎都不同,并且它不会给出我期望的结果:
Private countObjects As Integer
Private Sub MyMainFunction()
Dim objects As List(Of MyObject)
'Query with Dapper, unrelevant to the problem.
Using connection As New System.Data.SqlClient.SqlConnection("aConnectionString")
objects = connection.Query("SELECT * FROM Foo") 'Returns around 81000 rows.
End Using
Parallel.ForEach(objects, Sub(u) MyParallelFunction(u))
Console.WriteLine(String.Format("Count : {0}", countObjects)) 'Prints "Count : 80035" or another incorrect count, which seems to differ on each execution of MyMainFunction.
End Sub
Private Sub MyParallelFunction(obj As MyObject)
Interlocked.Increment(countObjects) 'Breakpoint …Run Code Online (Sandbox Code Playgroud) 我有一些代码令人沮丧的问题,不知道为什么会出现这个问题.
//
// .NET FRAMEWORK v4.6.2 Console App
static void Main( string[] args )
{
var list = new List<string>{ "aa", "bbb", "cccccc", "dddddddd", "eeeeeeeeeeeeeeee", "fffff", "gg" };
foreach( var item in list )
{
Progress( item );
}
}
private static int _cursorLeft = -1;
private static int _cursorTop = -1;
public static void Progress( string value = null )
{
lock( Console.Out )
{
if( !string.IsNullOrEmpty( value ) )
{
Console.Write( value );
var left = Console.CursorLeft;
var …Run Code Online (Sandbox Code Playgroud) 在阅读InterlockedIncrement函数时,我看到了传递的变量必须在32位边界上对齐的注释.通常我见过使用InterlockedIncrement的代码,如下所示:
class A
{
public:
A();
void f();
private:
volatile long m_count;
};
A::A() : m_count(0)
{
}
void A::f()
{
::InterlockedIncrement(&m_count);
}
Run Code Online (Sandbox Code Playgroud)
以上代码在多处理器系统中是否正常工作,还是应该更加关注?
我已经在VB.NET中做了一段时间的简单多线程,并刚刚进入我的第一个大型多线程项目.我总是使用Synclock声明完成所有事情,因为我认为没有更好的方法.
我刚刚了解了这个Interlocked类 - 它看起来好像这一切:
Private SomeInt as Integer
Private SomeInt_LockObject as New Object
Public Sub IntrementSomeInt
Synclock SomeInt_LockObject
SomeInt += 1
End Synclock
End Sub
Run Code Online (Sandbox Code Playgroud)
可以用单个语句替换:
Interlocked.Increment(SomeInt)
Run Code Online (Sandbox Code Playgroud)
这会在内部处理所有锁定并修改数字.这比为简单操作编写自己的锁要简单得多(更长时间运行或更复杂的操作显然仍然需要自己的锁定).
当我可以使用这些Interlocked方法完成同样的事情时,我是否有理由使用专用锁定对象来进行自己的锁定?
interlocked ×10
c# ×7
.net ×4
atomicity ×1
c++ ×1
concurrency ×1
java ×1
locking ×1
multicore ×1
optimization ×1
release ×1
synclock ×1
vb.net ×1
winapi ×1