我有一个带有支持字段的属性,我想让线程安全(获取和设置)。 除设置和返回外,get和set方法没有逻辑。
我认为有两种方法可以在属性self(volatile和lock)中封装逻辑。 我对这两个人的理解是正确的还是我犯了什么错误?
以下是我的例子:
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;
}
}
}
}
答案 0 :(得分:3)
有关mor信息,请参阅great site by 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!
请注意,应用volatile不会阻止写入后读取交换,这可以创建脑筋急转弯。 Joe Duffy通过以下示例很好地说明了问题:如果Test1
和Test2
同时在不同的线程上运行,则a和b都可能最终得到值0(尽管使用了volatile在x
和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)
...
}
}
MSDN文档指出使用volatile关键字可确保始终在字段中显示最新值。这是不正确的,因为正如我们所看到的,可以重新排序读取后写入。
这提供了一个避免volatile的强有力的案例:即使您理解此示例中的细微之处,处理您的代码的其他开发人员是否也能理解它? Test1
和Test2
(或锁定)中两个分配中的每个分配之间的完整范围可以解决问题。
传递引用参数或捕获的局部变量不支持volatile
关键字:在这些情况下,您必须使用VolatileRead
和VolatileWrite
方法。