Kla*_*aus 1 .net c# multithreading locking volatile
我有一个带有后备字段的属性,我想让它确保线程安全(获取和设置)。get 和 set 方法除了设置和返回没有逻辑。
我认为有两种方法可以将逻辑封装在属性 self(易失性和锁定)中。我对两者的理解是正确的还是我犯了任何错误?
下面是我的例子:
public class ThreadSafeClass
{
// 1. Volatile Example:
private volatile int _processState_1;
public int ProcessState_1
{
get { return _processState_1; }
set { _processState_1 = value; }
}
// 2. Locking Example:
private readonly object _processState_2Lock = new object();
private int _processState_2;
public int ProcessState_2
{
get
{
lock (_processState_2Lock)
{
return _processState_2;
}
}
set
{
lock (_processState_2Lock)
{
_processState_2 = value;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅J. Albahari的伟大网站:
同步结构可以分为四类:
简单的阻塞方法:
这些等待另一个线程完成或等待一段时间过去。Sleep, Join, 和Task.Wait是简单的阻塞方法。
锁定结构:
这些限制了一次可以执行某些活动或执行一段代码的线程数。排他锁结构是最常见的——它们一次只允许一个线程进入,并允许相互竞争的线程访问公共数据而不会相互干扰。标准的排他锁结构是lock( Monitor.Enter/ Monitor.Exit) Mutex、 和SpinLock。非排他性locking结构是Semaphore,SemaphoreSlim和reader/writer锁。
信号结构:
这些允许一个线程暂停直到从另一个线程接收到通知,从而避免了低效轮询的需要。有两种常用的信号设备:事件等待句柄和监视器的等待/脉冲方法。Framework 4.0 引入了CountdownEvent和Barrier类。
非阻塞同步结构:
它们通过调用处理器原语来保护对公共字段的访问。CLR 和 C# 提供以下非阻塞结构:Thread.MemoryBarrier、Thread.VolatileRead、Thread.VolatileWrite、volatile关键字和Interlocked类。
该volatile关键字:
volatile 关键字指示编译器在每次读取该字段时生成一个获取栅栏,并在每次写入该字段时生成一个释放栅栏。获取栅栏防止其他读/写在栅栏之前移动;释放栅栏可防止在栅栏之后移动其他读/写。这些“半围栏”比全围栏更快,因为它们为运行时间和硬件提供了更多优化空间。
碰巧的是,英特尔的 X86 和 X64 处理器总是对读取应用获取栅栏,对写入应用释放栅栏——无论您是否使用 volatile 关键字——因此,如果您使用这些处理器,该关键字对硬件没有影响。但是,
volatile它确实会对编译器和 CLR 执行的优化以及 64 位 AMD 和(在更大程度上)安腾处理器产生影响。这意味着您的客户端运行特定类型的 CPU,您不能更轻松。
将 volatile 应用于字段的效果可以总结如下:
First instruction Second instruction Can they be swapped?
Read Read No
Read Write No
Write Write No (The CLR ensures that write-write operations are never swapped, even without the volatile keyword)
Write Read Yes!
Run Code Online (Sandbox Code Playgroud)
请注意,应用 volatile 不会阻止写入后读取的交换,这可能会造成脑筋急转弯。Joe Duffy 用下面的例子很好地说明了这个问题:如果Test1和Test2同时在不同的线程上运行,a 和 b 的最终值可能都是 0(尽管在x和上都使用了 volatile y):
class IfYouThinkYouUnderstandVolatile
{
volatile int x, y;
void Test1() // Executed on one thread
{
x = 1; // Volatile write (release-fence)
int a = y; // Volatile read (acquire-fence)
...
}
void Test2() // Executed on another thread
{
y = 1; // Volatile write (release-fence)
int b = x; // Volatile read (acquire-fence)
...
}
}
Run Code Online (Sandbox Code Playgroud)
MSDN 文档指出,使用 volatile 关键字可确保字段中始终存在最新值。这是不正确的,因为正如我们所看到的,写入后读取可以重新排序。
这为避免 volatile 提供了一个强有力的理由:即使您理解了这个示例中的微妙之处,其他处理您代码的开发人员也会理解它吗?Test1和Test2(或锁)中的两个分配中的每一个之间的完整围栏解决了这个问题。
该volatile关键字不被支持与通按引用参数或捕获局部变量:在这种情况下,你必须使用VolatileRead和VolatileWrite方法。
| 归档时间: |
|
| 查看次数: |
4454 次 |
| 最近记录: |