C# 如何在此模型中使用volatile关键字?
我有一个数据类,里面有很多数据(电视节目表数据)。 数据从一端查询,从另一端定期更新。 有两个线程:第一个线程根据请求查询数据,第二个线程定期更新数据。 为了防止锁定,我使用了数据类的两个实例(副本):live实例和backup实例。 最初,两个实例都用相同的数据填充。第一个线程只读取活动实例。 第二个线程定期更新两个实例,如下所示:C# 如何在此模型中使用volatile关键字?,c#,volatile,C#,Volatile,我有一个数据类,里面有很多数据(电视节目表数据)。 数据从一端查询,从另一端定期更新。 有两个线程:第一个线程根据请求查询数据,第二个线程定期更新数据。 为了防止锁定,我使用了数据类的两个实例(副本):live实例和backup实例。 最初,两个实例都用相同的数据填充。第一个线程只读取活动实例。 第二个线程定期更新两个实例,如下所示: 更新备份实例 交换备份和活动实例(即备份实例成为活动实例) 更新备份实例 备份实例和活动实例现在都是最新的 我的问题是:我应该如何在这里使用volatile关
- 更新备份实例
- 交换备份和活动实例(即备份实例成为活动实例)
- 更新备份实例
- 备份实例和活动实例现在都是最新的
public class data
{
// Lots of fields here.
// Should these fields also be declared volatile?
}
我已经将这些引用变为易变的:
public volatile data live
public volatile data backup
如果您计划在
lock
s之外或在互锁的情况下对字段进行修改,则应声明字段为volatile
。下面是一篇深入解释volatile的最好文章:老实说,我只想锁定它。正确性更容易检查,而且不需要备份
根据您的计划,字段也必须是不稳定的。请考虑以下情况:
public class Data
{
public int SimpleInt;
}
为了简单起见,这里只有一个公共字段,这同样适用于更现实的结构。(顺便提一下,在C#中,类名称的大写是一种更常见的约定)
现在考虑“代码> Leavy.SimuleIt/Cuth>线程A所见,因为Lave可以缓存,我们需要使它具有易失性。但是,考虑当对象用<代码>备份< /代码>交换,然后交换回 Lave 时, Leave<代码>将具有与以前相同的内存位置(除非GC已经移动它)。因此live.SimpleInt
将具有与以前相同的内存位置,因此如果它不是易失性的,线程A可能正在使用缓存版本的live.SimpleInt
但是,如果您创建了一个新的数据对象,而不是进行交换,则live.SimpleInt
的新值将不在线程的缓存中,并且可以安全地不易失性
<强>同样重要的是要考虑字段的字段也必须是易失性的。< /强>
实际上,现在您只需要一个存储的数据对象。新线程将被创建为仅由一个线程引用的对象(因此它不会被另一个线程损坏或对另一个线程造成损坏),并且它的创建将基于从live
读取的值,这也是安全的,因为另一个线程仅在读取(除非使用一些表示“读取”的回忆录技术)是真正的幕后写操作,读操作不会影响其他读操作,尽管它们可能会受到写操作的影响)在对单个线程可见的情况下进行更改,因此只有最终写操作才需要考虑同步问题,因为只有volatile或MemoryBarrier用于保护时,同步才应该是安全的,因为分配引用是原子性的,而且您不再关心旧的值。我认为,通过使用volatile
标记对象,您不会得到想要的效果。考虑这个代码。
volatile data live;
void Thread1()
{
if (live.Field1)
{
Console.WriteLine(live.Field1);
}
}
在上面的示例中,如果在第一个线程进入if
并调用console.WriteLine
期间,第二个线程交换了live
和backup
引用,则可以将false
写入控制台
如果这个问题与您无关,那么您真正需要做的就是将live
变量标记为volatile
。您不需要将数据中的各个字段标记为易失性
。原因是因为易失性读取会创建获取围栏内存屏障,而易失性写入会创建释放围栏内存屏障。这意味着,当线程2交换引用时,必须首先提交对数据的各个字段的所有写入,当线程1想要读取活动实例的各个字段时,必须首先从主存重新获取活动变量。您不需要将backup
变量标记为volatile
,因为线程1从未使用过它
本文详细介绍了volatile
的语义,并应解释为什么只需将live
引用标记为该引用。volatile在多线程环境中几乎毫无用处@这篇论文是关于C++中的易失性的。虽然我也不倾向于在这里使用volatile,但C#中的volatile没有相同的语义。使用volatile只会使它更快地失败。需要一把锁。不,这可以无锁完成。这不是我喜欢的方法,但它可以。