C# 如何在此模型中使用volatile关键字?

C# 如何在此模型中使用volatile关键字?,c#,volatile,C#,Volatile,我有一个数据类,里面有很多数据(电视节目表数据)。 数据从一端查询,从另一端定期更新。 有两个线程:第一个线程根据请求查询数据,第二个线程定期更新数据。 为了防止锁定,我使用了数据类的两个实例(副本):live实例和backup实例。 最初,两个实例都用相同的数据填充。第一个线程只读取活动实例。 第二个线程定期更新两个实例,如下所示: 更新备份实例 交换备份和活动实例(即备份实例成为活动实例) 更新备份实例 备份实例和活动实例现在都是最新的 我的问题是:我应该如何在这里使用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只会使它更快地失败。需要一把锁。不,这可以无锁完成。这不是我喜欢的方法,但它可以。