C# 我们需要使所有方法都异步吗

C# 我们需要使所有方法都异步吗,c#,async-await,task-parallel-library,C#,Async Await,Task Parallel Library,我实现了一个Api,它有一个控制器,控制器有一个如下所示的调用树 public async Task<Collection<United.Service.Presentation.LoyaltyModel.Program>> GetRewardPrograms(string languageCode) { return await _referenceDataDomain.GetRewardPrograms(lang

我实现了一个Api,它有一个控制器,控制器有一个如下所示的调用树

public async Task<Collection<United.Service.Presentation.LoyaltyModel.Program>> GetRewardPrograms(string languageCode)
{
           
                return await _referenceDataDomain.GetRewardPrograms(languageCode).ConfigureAwait(false);
            
          
}
公共异步任务GetRewardPrograms(字符串语言代码)
{
返回wait\u referenceDataDomain.GetRewardPrograms(languageCode).ConfigureWait(false);
}
公共异步任务GetRewardPrograms(字符串语言代码)
{
返回wait\u referenceDataProvider.GetRewardPrograms(languageCode).ConfigureWait(false);
}
公共异步任务GetRewardPrograms(字符串语言代码)
{
if(string.IsNullOrEmpty(languageCode))
{
languageCode=\u常量。语言\u代码\u EN\u US;
}
var rewardProgramsSet=新集合();
var format=CacheKeyDictionary.CacheKeyFormat(CacheKeyDictionary.RewardPrograms);
var cacheKey=string.Format(格式,语言代码);
var cacheValue=await\u cacheUtility.getcacheitemsync(cacheKey).ConfigureAwait(false);
if(cacheValue!=null&&cacheValue.Any())
返回缓存值;
if(_commonConfig.useloyltyservice())
{
尝试
{
var cslHttpClient=_serviceProvider.GetRequiredService();
var queryStringParams=new NameValueCollection(){{{“languageCode”,languageCode};
var result=await cslHttpClient.GetAsync(\u commonConfig.LoyaltyServiceUrl(),
“ReferenceDataRewardProgram/idType/a”,queryStringParams,_commonConfig.TimeOutDefault());
if(result!=null&&result.ResponseData!=null&&result.ResponseData.referenceDataRewardProgramList.Count>0)
{
foreach(result.ResponseData.referenceDataRewardProgramList中的变量项)
{
var程序=新程序
{
ProgramID=item.ProgramID.ToInt32(),
代码=item.ProgramCode,
描述=项目。描述,
语言=新语言{LanguageCode=LanguageCode}
};
奖励计划set.Add(计划);
}
}
}
捕获{}
}
if(rewardProgramsSet!=null&&rewardProgramsSet.Any())
{
wait _cacheUtility.SetCacheItemAsync(cacheKey,rewardProgramsSet).ConfigureWait(false);
}
返回奖励程序集;
}
我得到一个代码评审反馈,由于我使所有方法都异步,并且第二个代码块没有执行任何异步操作,我应该返回任务,而不是使用异步方法,因为添加异步将在内部使我的方法进入状态机,这将产生一些性能问题,评审员在本文中引用了我
,如果上述方法存在缺陷,请有人指导我。

我通过Stephan发布的这篇文章得到了答案,感谢Jonathan提供链接

以上链接中的以下段落是对我问题的回答:

建议指引 我建议遵循这些准则:

默认情况下不要删除。使用async和Wait来获得自然、易于阅读的代码。 当该方法仅仅是通过或过载时,请考虑ELIDEN。< /P>
// Simple passthrough to next layer: elide.
Task<string> PassthroughAsync(int x) => _service.PassthroughAsync(x);

// Simple overloads for a method: elide.
async Task<string> OverloadsAsync(CancellationToken cancellationToken)
{
    ... // Core implementation, using await.
}
Task<string> OverloadsAsync() => OverloadsAsync(CancellationToken.None);

// Non-trivial passthrough: use keywords.
async Task<string> PassthroughAsync(int x)
{
    // Reasoning: GetFirstArgument can throw.
    //  Even if it doesn't throw today, some yahoo can change it tomorrow, and it's not possible for them to know to change *this* method, too.
    return await _service.PassthroughAsync(GetFirstArgument(), x);
}

// Non-trivial overloads for a method: use keywords.
async Task<string> OverloadsAsync()
{
    // Same reasoning as above; GetDefaultCancellationTokenForThisScope can throw.
    return await OverloadsAsync(GetDefaultCancellationTokenForThisScope());
}
//到下一层的简单传递:elide。
任务PassthroughAsync(intx)=>\u service.PassthroughAsync(x);
//方法的简单重载:elide。
异步任务重载SASync(CancellationToken CancellationToken)
{
…//核心实现,使用wait。
}
Task OverloadsAsync()=>OverloadsAsync(CancellationToken.None);
//非平凡传递:使用关键字。
异步任务PassthroughAsync(int x)
{
//推理:GetFirstArgument可以抛出。
//即使它今天不抛出,一些雅虎明天可以改变它,他们也不可能知道要改变这个方法。
return wait_service.PassthroughAsync(GetFirstArgument(),x);
}
//方法的非平凡重载:使用关键字。
异步任务重载sasync()
{
//与上面的推理相同;GetDefaultCancellationTokenForThisScope可以抛出。
return wait OverloadsAsync(getdefaultcancellationtokenforthiscope());
}

@Dave感谢您的回复,更新了第二个块,它有异步限定符,在这种特殊情况下,它不做任何事情,只是返回结果,但在其他情况下,我们实现了业务逻辑,并且我们的所有方法都通过域对象使用refdataprovider,因此为了一致性,我们进行了此调用,这称为任务转发,它保存了一个
IAsyncStateMachine
实现,并为您提供了一个小的效率,但需要注意的是,该方法不能抛出自己,否则您需要捕获异常并将其放在任务上,以遵守异步和等待模式的语义。我相信有一个斯蒂芬·克利里(Stephen Cleary)的博客涉及到这个话题。评论只是说,在第二个示例中,您可以安全地删除
async
wait
关键字来转发任务,正如Michael所说,在这些场景中删除async可能会对性能产生很小的影响(但它很小,因此您需要经常调用它或在性能敏感的上下文中注意)-我曾经读过这样一篇文章,在每一个级别上展开都会对异常处理和堆栈跟踪产生一些影响,但我记不起来了,这很可能和Michael说的是同一件事。这是一篇非常好的文章:@saurabhvats链接到一篇文章并不是一个真正的答案。请随意添加您自己的:)总结文章中哪些内容真正解决了您的公关问题通常是最佳做法
 public async Task<Collection<Program>> GetRewardPrograms(string languageCode)
        {
           
            if (string.IsNullOrEmpty(languageCode))
            {
                languageCode = _constants.LANGUAGE_CODE_EN_US;
            }
            var rewardProgramsSet = new Collection<Program>();
            var format = CacheKeysDictionary.CacheKeyFormat(CacheKeysDictionary.RewardPrograms);
            var cacheKey = string.Format(format, languageCode);

            var cacheValue = await _cacheUtility.GetCacheItemAsync<Collection<Program>>(cacheKey).ConfigureAwait(false);
            if (cacheValue != null && cacheValue.Any())
                return cacheValue;

            if (_commonConfig.UseLoyaltyService())
            {
                try
                {
                    var cslHttpClient = _serviceProvider.GetRequiredService<ICSLServiceProxy>();
                    var queryStringParams = new NameValueCollection() { { "languageCode", languageCode } };

                    var result = await cslHttpClient.GetAsync<NameValueCollection, RewardProgramsReferenceData>(_commonConfig.LoyaltyServiceUrl(),
                                                "ReferenceDataRewardProgram/idType/a", queryStringParams, _commonConfig.TimeOutDefault());

                    if (result != null && result.ResponseData != null && result.ResponseData.referenceDataRewardProgramList.Count > 0)
                    {
                        foreach (var item in result.ResponseData.referenceDataRewardProgramList)
                        {
                            var program = new Program
                            {
                                ProgramID = item.ProgramID.ToInt32(),
                                Code = item.ProgramCode,
                                Description = item.Description,
                                Language = new Language { LanguageCode = languageCode }
                            };

                            rewardProgramsSet.Add(program);
                        }
                    }
                }
                catch { }
            }

            if (rewardProgramsSet != null && rewardProgramsSet.Any())
            {
                await _cacheUtility.SetCacheItemAsync(cacheKey, rewardProgramsSet).ConfigureAwait(false);
            }

            return rewardProgramsSet;
        }
// Simple passthrough to next layer: elide.
Task<string> PassthroughAsync(int x) => _service.PassthroughAsync(x);

// Simple overloads for a method: elide.
async Task<string> OverloadsAsync(CancellationToken cancellationToken)
{
    ... // Core implementation, using await.
}
Task<string> OverloadsAsync() => OverloadsAsync(CancellationToken.None);

// Non-trivial passthrough: use keywords.
async Task<string> PassthroughAsync(int x)
{
    // Reasoning: GetFirstArgument can throw.
    //  Even if it doesn't throw today, some yahoo can change it tomorrow, and it's not possible for them to know to change *this* method, too.
    return await _service.PassthroughAsync(GetFirstArgument(), x);
}

// Non-trivial overloads for a method: use keywords.
async Task<string> OverloadsAsync()
{
    // Same reasoning as above; GetDefaultCancellationTokenForThisScope can throw.
    return await OverloadsAsync(GetDefaultCancellationTokenForThisScope());
}