可以Lazy<比;和Task<比;组合以延迟数据库查找

本文关键字:延迟 数据库 查找 Lazy 可以 Task 组合 | 更新日期: 2023-09-27 18:19:24

我已经编写了下面这个问题的简化版本。所以我有这个服务,在方法DoSomething中,我想做一个同步检查(IsTrue()方法),然后是async调用(数据库查找)。

如果syncResult为假,我只想做数据库查找(EF6异步选择-由this.SuperSlowThing模拟)。这可以通过使用&&来完成,只有在syncResult为真时才会继续使用asyncResult

我的问题是:这是解决这个问题的好方法吗?如果有,有没有更简单的方法?

internal class MyService
{
    public void DoSomething(bool someValue)
    {
        var syncResult = this.IsTrue(someValue);
        var asyncResult = new Lazy<Task<string>>(this.SuperSlowThing);
        if (syncResult && asyncResult.Value.Result.Length > 0)
        {
            Console.WriteLine("Did SuperSlowThing - result is " + asyncResult.Value.Result);
        }
        else
        {
            Console.WriteLine("Didn't do SuperSlowThing");
        }            
    }
    private bool IsTrue(bool someValue)
    {
        return someValue;
    }
    private Task<string> SuperSlowThing()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        while (sw.ElapsedMilliseconds < 5000)
        {
            Thread.Sleep(500);
            Console.WriteLine("Delaying");
        }
        return Task.FromResult("Done!");
    }
}

编辑

那么让我来详细说明一下这些代码在现实生活中的作用。DoSomething方法实际上是一个数据库查找。如果someValue是网络中存在的某个数字。然后在数据库中查找表a中的数字。这是同步操作。

如果在表a中找到结果,则返回该结果,否则查找表b中的数字。

如果在web中没有找到someValue的编号。

可以Lazy<比;和Task<比;组合以延迟数据库查找

我认为这里没有真正的理由使用Lazy。你可以先检查syncResult然后再检查asyncResult:

public async Task DoSomething(bool someValue)
{
    if (IsTrue(someValue) && await SuperSlowThing().Length > 0)
    {
       // ..
    }
    else
    {
       // ..
    }            
}

或者一个更好的,更可读的版本:

public async Task DoSomething(bool someValue)
{
    bool condition = false;
    if (IsTrue(someValue))
    {
        var result = await SuperSlowThing();
        if (result.Length > 0)
        {
            Console.Writeline(result);
        }
        condition = true;
    }
    if (!condition)
    {
        // ..
    }
}

Lazy在多线程情况下很有用,或者你不完全确定是否/何时使用它。在您的情况下,您确实知道是否在该方法的范围内使用它,因此使用Lazy只会使事情变得复杂。

是的,Lazy适合这种情况。您的示例代码原则上是好的。它有一个问题,它使用.Result而不是await。但那只是一个细节,与本次讨论无关。

如果你想避免在这里使用Lazy,让我建议一个替代方案:

string slowResult;
if (IsTrue(someValue) && (slowResult = await SuperSlowThing()).Length > 0)
{
   Console.WriteLine(slowResult);
}

注意,您可以多次访问slowResult而无需重新执行SuperSlowThing。这就是你使用Lazy的目的。

c# 6甚至允许你声明一个内联变量:

if (IsTrue(someValue) && (var slowResult = await SuperSlowThing()).Length > 0)

(至少我是这么认为的)

如果您发现嵌套赋值令人困惑(这是可以理解的):

if (IsTrue(someValue))
{
   string slowResult = await SuperSlowThing();
   if (slowResult.Length > 0)
     Console.WriteLine(slowResult);
}

由于等待,这个问题实际上与同步代码中的问题相同。这不是关于异步或任务。写await SuperSlowThingAsync()还是SuperSlowThing对方法的逻辑流程没有影响。忽略思维中的异步性

如果是,有没有更简单的方法?

你的解决方案究竟实现了什么,一个简单的if构造不能?

var syncResult = this.IsTrue(someValue);
if (syncResult)
{
    var result = this.SuperSlowThing();
    Console.WriteLine("Did SuperSlowThing - result is " + result);
}
else
{
    Console.WriteLine("Didn't do SuperSlowThing");
}  

如果您的目标是封装一个按需异步检索的值,那么Lazy<Task<T>>是一个合适的抽象。从你的问题中很难判断这是否是你真正想要的。

考虑这两个:

var x = new Lazy<int>(
    () => Task.Run(
        () => ComputeValue()));
var y = Task.Run(
    () => ComputeValue());

其中ComputeValue是一个昂贵的函数,返回一个int

则分配给y的值将立即开始计算。每个等待y的人都在等待一个已经启动的Task

分配给x的值只会在第一次有人请求该值时才开始计算,但是每个人在开始计算后仍然会等待相同的计算

Lazy与同步或异步无关,它与延迟执行有关。在你的情况下,你根本没有推迟执行。您正在创建Lazy参数,然后立即访问它(假设您的值为true)。只要输入

if(condition)
{
  var = await SuperSlowThing()
}