C# 我正在寻找一个懒惰的、线程安全的实现来缓存昂贵计算的第一个非空结果

C# 我正在寻找一个懒惰的、线程安全的实现来缓存昂贵计算的第一个非空结果,c#,thread-safety,singleton,C#,Thread Safety,Singleton,我对使用singleton非常陌生,很难理解C#中singleton的惰性实现 假设我有一个最初为null/empty的字符串,当有人对该字符串执行get调用时,我必须仅在该字符串为null/empty时计算该字符串,否则返回现有字符串 我的正常实现如下所示 public class A { private string str = null; public A() { } public string GetStr()

我对使用singleton非常陌生,很难理解C#中singleton的惰性实现

假设我有一个最初为null/empty的字符串,当有人对该字符串执行get调用时,我必须仅在该字符串为null/empty时计算该字符串,否则返回现有字符串

我的正常实现如下所示

public class A
{
       private string str = null;

       public A()
       {
       }

       public string GetStr()
       {
            if(String.IsNullOrEmpty(str)) 
            {
                str = CalculateStr();
            }
            return str;
       }
}
如何实现上述示例的线程安全版本

编辑#1:
calculatest()
可以返回空字符串。如果是这样的话,我们下次需要重新计算

编辑#2:用例是变量str应该是线程安全的,并且应该仅在其不为null/空时计算

编辑#3:我不知道它是否被称为singleton,我知道上面提供的示例不是线程安全的。

对于昂贵调用的(确定性)结果的缓存,请使用-这有一个可选的
LazyThreadSafetyMode
参数,允许您指定如何解决并发问题

更新-假设
CalculateStr
不是静态的


请注意,以上两种情况都不需要单例实例-可以根据需要调用尽可能多的
a
实例,并且每个
a
实例将(最终)缓存从
CalculateStr
返回的单个非空值。如果需要单例,则共享
a
实例,或使用IoC容器控制a的单例。

首先,您的str应该是静态的,以成为“单例”。 其次,您可以在中使用
Lazy

然后像这样定义singleton

  private static readonly Lazy<string>
    str =
    new Lazy<string>
        (() => CalculateStr());
私有静态只读延迟
str=
新懒汉
(()=>calculatest());

通过使用
Lazy
,您可以在不使用锁的情况下归档线程安全。

现代C#in.NET内核中最简单的惰性单例实现如下:

public class A
{
    public static readonly string LazyStr = CalculateStr();

    private static string CalculateStr(){}
}
LazyStr
变量将仅在您第一次需要它时初始化(因为静态只读关键字),之后,它将始终保持不变

请尝试以下简单示例:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"Start at {DateTime.Now}");

        Console.ReadKey();

        Console.WriteLine(A.LazyString);

        Console.ReadKey();

        Console.WriteLine(A.LazyString);

        Console.ReadKey();
    }
}

public class A
{
    public static readonly String LazyString = CalculateString();

    private static string CalculateString()
    {
        return DateTime.Now.ToString();
    }
}

你的问题是什么?这是一个怎样的单身汉?我假设这不是所有的代码;因此,
private A()
@JohnathanBarclay编辑了代码。我正在寻找一个懒惰的线程安全实现,我想这对您来说会很有趣:
静态
不是必需的;无论如何只会有一个实例。我只是指出“singleton”应该与static一起工作,以确保程序中只有一个实例是的。我见过类似的例子,但是如何在字符串上添加像nullorempty这样的检查呢?谢谢,@starkkk92我没弄错吧:
calculatest()
返回一个空字符串,我正在读那条评论,可能是这样的?如果是这样,您想在下一次调用
GetStr
时重新计算它吗?是的,CalculateStr()可以返回null/empty,我们必须在下一次这种情况下重新计算。@starkkk92我们需要抛弃Lazy(不幸的是),以满足您的额外要求,即如果
CalculateStr
的结果为null或空,则不缓存它。我已经更新了手动双重检查锁。谢谢。我继续使用双重检查锁定。虽然它是singleton的一个有效实现,但它并不懒惰,因为静态字段在第一次使用类之前初始化,并且LazyStr在您第一次需要时初始化的语句不是真的。如果它们是静态只读的,则不是。检查我发布的示例程序。字符串仅在您第一次调用它时初始化。您是否检查了程序的输入?只需在打印第一个日期之前延迟一段时间就可以看出差异。您可以看一看示例。为了给属性添加一些惰性程度,您应该添加静态构造函数,该构造函数强制不将type标记为beforefieldinit之前的
,并且固有地禁止静态字段的早期初始化。请使用.NET core中C#7或更高版本的真实编译器进行尝试
public class A
{
    public static readonly string LazyStr = CalculateStr();

    private static string CalculateStr(){}
}
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine($"Start at {DateTime.Now}");

        Console.ReadKey();

        Console.WriteLine(A.LazyString);

        Console.ReadKey();

        Console.WriteLine(A.LazyString);

        Console.ReadKey();
    }
}

public class A
{
    public static readonly String LazyString = CalculateString();

    private static string CalculateString()
    {
        return DateTime.Now.ToString();
    }
}