gpt4 book ai didi

c# - 从互锁变量中读取最新值,只对变量进行一次写入

转载 作者:太空狗 更新时间:2023-10-29 21:58:29 25 4
gpt4 key购买 nike

我想用两种方法创建一个类:

  • void SetValue(T value)存储一个值,但只允许存储单个值(否则会抛出异常)。
  • T GetValue()检索值(如果还没有值则抛出异常)。

我有以下愿望/限制:

  • 读取值应该是廉价的。
  • 写入值可能(适度)昂贵。
  • GetValue()只有当最新值不存在时才应该抛出异常(null):它不应该基于陈旧的null抛出异常调用 SetValue() 后的值在另一个线程中。
  • 该值只写入一次。这意味着 GetValue()如果该值不为空,则不需要刷新该值。
  • 如果可以避免完全的内存屏障,那就(好)多了。
  • 我知道无锁并发性更好,但我不确定这里是否是这种情况。

我想出了几种方法来实现这一点,但我不确定哪些是正确的,哪些是有效的,为什么它们(不)正确和(不)有效,以及是否有更好的方法来实现什么我要。

方法一

  • 使用非 volatile 字段
  • 使用 Interlocked.CompareExchange写信给外地
  • 使用 Interlocked.CompareExchange从现场阅读
  • 这依赖于(可能是错误的)假设,即在执行 Interlocked.CompareExchange(ref v, null, null) 之后在一个字段上将导致下一次访问获得的值至少与 Interlocked.CompareExchange 的值一样新。锯。

代码:

public class SetOnce1<T> where T : class
{
private T _value = null;

public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
// Read an up-to-date value of that variable
Interlocked.CompareExchange<T>(ref _value, null, null);
// _value contains up-to-date data, because of the Interlocked.CompareExchange call above.
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
}

// _value contains up-to-date data here too.
return _value;
}

public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}

if (Interlocked.CompareExchange<T>(ref _value, newValue, null) != null) {
throw new System.Exception("Value already present.");
}

return newValue;
}
}

方法二

  • 使用 volatile领域
  • 使用 Ìnterlocked.CompareExchange to write the value (with [Joe Duffy](http://www.bluebytesoftware.com/blog/PermaLink,guid,c36d1633-50ab-4462-993e-f1902f8938cc.aspx)'s #pragma to avoid the compiler warning on passing a volatile value by引用`).
  • 直接读取值,因为字段是 volatile

代码:

public class SetOnce2<T> where T : class
{
private volatile T _value = null;

public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}

public T SetValue(T newValue) {
if (newValue == null) {
throw new System.ArgumentNullException();
}

#pragma warning disable 0420
T oldValue = Interlocked.CompareExchange<T>(ref _value, newValue, null);
#pragma warning restore 0420

if (oldValue != null) {
throw new System.Exception("Value already present.");
}
return newValue;
}
}

方法三

  • 使用非 volatile 字段。
  • 写入时使用锁。
  • 如果读取 null,则在读取时使用锁(由于引用是原子读取的,因此我们将在锁外获得一致的值)。

代码:

public class SetOnce3<T> where T : class
{
private T _value = null;

public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
lock (this) {
// Read an up-to-date value of that variable
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}
}
return _value;
}

public T SetValue(T newValue) {
lock (this) {
if (newValue == null) {
throw new System.ArgumentNullException();
}

if (_value != null) {
throw new System.Exception("Value already present.");
}

_value = newValue;

return newValue;
}
}
}

方法四

  • 使用可变字段
  • 使用锁写入值。
  • 直接读取值,因为字段是可变的(即使我们不使用锁,我们也会得到一个连贯的值,因为引用是原子读取的)。

代码:

public class SetOnce4<T> where T : class
{
private volatile T _value = null;

public T GetValue() {
if (_value == null) {
throw new System.Exception("Value not yet present.");
}
return _value;
}

public T SetValue(T newValue) {
lock (this) {
if (newValue == null) {
throw new System.ArgumentNullException();
}

if (_value != null) {
throw new System.Exception("Value already present.");
}

_value = newValue;

return newValue;
}
}
}

其他方法

我也可以使用 Thread.VolatileRead() 结合任何书写技巧来读取值。

最佳答案

好吧,不确定波动性,但如果您不介意有点滥用并调用第二种方法...(而且它不依赖于可空性;可自由用于值类型)也避免在 getter 中进行空检查。只在写入时进行锁定,所以据我所知,唯一的负面影响来自在获取值时调用委托(delegate)。

public class SetOnce<T>
{
private static readonly Func<T> NoValueSetError = () => { throw new Exception("Value not yet present.");};

private Func<T> ValueGetter = NoValueSetError;
private readonly object SetterLock = new object();

public T SetValue(T newValue)
{
lock (SetterLock)
{
if (ValueGetter != NoValueSetError)
throw new Exception("Value already present.");
else
ValueGetter = () => newValue;
}

return newValue;
}

public T GetValue()
{
return ValueGetter();
}
}

其实我觉得这个真的很傻,感觉有点虐。我很想看到有关这样做的潜在问题的评论。 :)

编辑:刚刚意识到这意味着第一次调用 SetValue(null) 意味着“null”将被视为有效值并将无一异常(exception)地返回 null。不确定这是否是您想要的(我不明白为什么 null 不是有效值,但如果您想避免它,只需在 setter 中进行检查即可;不需要 setter/getter )

EDITx2:如果您仍想将其限制为 class 并避免使用 null 值,一个简单的更改可能是:

public class SetOnce<T> where T : class
{
private static readonly Func<T> NoValueSetError = () => { throw new Exception("Value not yet present.");};

private Func<T> ValueGetter = NoValueSetError;
private readonly object SetterLock = new object();

public T SetValue(T newValue)
{
if (newValue == null)
throw new ArgumentNullException("newValue");

lock (SetterLock)
{
if (ValueGetter != NoValueSetError)
throw new Exception("Value already present.");
else
ValueGetter = () => newValue;
}

return newValue;
}

public T GetValue()
{
return ValueGetter();
}
}

关于c# - 从互锁变量中读取最新值,只对变量进行一次写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15005083/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com