在内部不使用await的异步方法,因此是同步的

时间:2018-02-14 16:47:44

标签: c# async-await

我听过很多关于Asyncawait的内容,并对此有基本的了解,但我的知识有限。

它目前分散在我正在处理的所有项目中,我试图了解使用async

awaited方法的含义

例如:

public async Task<int> getNumber()
{
     return await getFirstNumber();    
}

public async Task<int> getFirstNumber()
{
     return 1;
}

请注意,在上面的示例中,两种方法都是async - 但只有getNumber调用等待。

这是一个问题,如果是,那么问题是什么?

如果getFirstNumber()进行数据库调用或其他I / O,或者其他可能很慢的事情,该怎么办?

这是我应该不惜一切代价避免的,或者有时候是否可以,如果是这样,在什么情况下?

编辑: 异步/等待警告未激活我的VS版本,所以我的代码运行正常。

2 个答案:

答案 0 :(得分:3)

这只是一个问题,因为async / await模式有一些与之相关的开销,在这种情况下并不常用。相反,您可以直接返回任务。

public Task<int> getFirstNumber()
{
     return Task.FromResult(1);
}

请记住,使用async/await只是实施细节并且不会影响&#34;界面&#34;或方法的面向公众的合同。因此,如果有一天您需要getFirstNumber()进行数据库调用,则可以在不违反方法签名设置的期望的情况下切换回使用async / await

如果您有兴趣,可以使用async / await模式获取getFirstNumber的IL代码:

getFirstNumber:
IL_0000:  newobj      UserQuery+<getFirstNumber>d__1..ctor
IL_0005:  stloc.0     
IL_0006:  ldloc.0     
IL_0007:  ldarg.0     
IL_0008:  stfld       UserQuery+<getFirstNumber>d__1.<>4__this
IL_000D:  ldloc.0     
IL_000E:  call        System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.Create
IL_0013:  stfld       UserQuery+<getFirstNumber>d__1.<>t__builder
IL_0018:  ldloc.0     
IL_0019:  ldc.i4.m1   
IL_001A:  stfld       UserQuery+<getFirstNumber>d__1.<>1__state
IL_001F:  ldloc.0     
IL_0020:  ldfld       UserQuery+<getFirstNumber>d__1.<>t__builder
IL_0025:  stloc.1     
IL_0026:  ldloca.s    01 
IL_0028:  ldloca.s    00 
IL_002A:  call        System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.Start<<getFirstNumber>d__1>
IL_002F:  ldloc.0     
IL_0030:  ldflda      UserQuery+<getFirstNumber>d__1.<>t__builder
IL_0035:  call        System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.get_Task
IL_003A:  ret         

<getFirstNumber>d__1.MoveNext:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+<getFirstNumber>d__1.<>1__state
IL_0006:  stloc.0     
IL_0007:  nop         
IL_0008:  ldc.i4.1    
IL_0009:  stloc.1     
IL_000A:  leave.s     IL_0024
IL_000C:  stloc.2     
IL_000D:  ldarg.0     
IL_000E:  ldc.i4.s    FE 
IL_0010:  stfld       UserQuery+<getFirstNumber>d__1.<>1__state
IL_0015:  ldarg.0     
IL_0016:  ldflda      UserQuery+<getFirstNumber>d__1.<>t__builder
IL_001B:  ldloc.2     
IL_001C:  call        System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.SetException
IL_0021:  nop         
IL_0022:  leave.s     IL_0039
IL_0024:  ldarg.0     
IL_0025:  ldc.i4.s    FE 
IL_0027:  stfld       UserQuery+<getFirstNumber>d__1.<>1__state
IL_002C:  ldarg.0     
IL_002D:  ldflda      UserQuery+<getFirstNumber>d__1.<>t__builder
IL_0032:  ldloc.1     
IL_0033:  call        System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Int32>.SetResult
IL_0038:  nop         
IL_0039:  ret         

<getFirstNumber>d__1.SetStateMachine:
IL_0000:  ret         

<getFirstNumber>d__1..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  nop         
IL_0007:  ret       

以下是使用Task.FromResult():

的样子
getFirstNumber:
IL_0000:  nop         
IL_0001:  ldc.i4.1    
IL_0002:  call        System.Threading.Tasks.Task.FromResult<Int32>
IL_0007:  stloc.0     
IL_0008:  br.s        IL_000A
IL_000A:  ldloc.0     
IL_000B:  ret         

答案 1 :(得分:1)

有些人说你的getFirstNumber方法中的任务是没用的,我会更进一步说,在你的方法中,等待任务并没有给你带来任何好处。 等待任务的想法通常与以下事实有关:

  • 您需要该任务的结果(适用于Task<TResult>),;

  • 您希望确保任务完全执行,以便您可以继续下一个语句;

我不认为你的例子就是这种情况。你可以简单地写:

public Task<int> getNumber()
{
     return getFirstNumber();    
}

public Task<int> getFirstNumber()
{
     return Task.FromResult(1);
}

在实际场景中,想象一下使用Entity Framework访问数据层的Repository类。使用这样的方法没有错:

public Task<int> GetLatestUserId ()
{
    var resultTask = db.Users.Select(u => u.Id).OrderByDesc(id => id).FirstAsync();
    return resultTask;
}

此方法的调用者可能必须等待此返回的任务才能使用互联网值(int I'd = await repository.GetLatestUserId()),因此,它必须是async方法。