【问题标题】:Implementing Awaitable Async method for NCache Get为 NCache Get 实现 Awaitable Async 方法
【发布时间】:2016-09-15 20:48:46
【问题描述】:

我们正在将所有或大部分 .NET 4.6 MVC WebAPI 控制器方法重构为async 方法。

这似乎适用于对可等待方法的较低级别调用的方法,例如 SQL 命令执行;但是我们正在使用 Alachisoft 的内存分布式缓存框架,称为 NCache(确切地说是 4.6 SP2),它不提供任何真正的异步方法。

是否值得创建一个 async 辅助方法来公开可等待的 Task<object> 返回类型?

传统上使用 NCache API,您在以下用法中通过 Key 从缓存中 Get 一个对象;

NCacheObject.Get(string);

建议是创建如下的辅助方法;

protected static async Task<Object> GetAsync(string key, Cache nCache)
{
    Task<Object> getTask = new Task<Object>(() => nCache.Get(key));
    getTask.Start();
    return await getTask.ConfigureAwait(false);
}

这样它将允许async 方法的完整瀑布流一直到入口控制器方法;

public static async Task<Tuple<List<SomeModel>, bool, string>> GetSomeModelList(string apiKey)
{
    return newTuple<List<SomeModel>, bool, string>(await GetAsync("GetSomeModelKey", CacheInstance).ConfigureAwait(false), true, "Success message");
}

最后是控制器方法;

[HttpGet, Route("Route/Method")]
public async Task<ResponseOutputModel<List<SomeModel>>> GetSomeModelList()
{
    ResponseOutputModel<List<SomeModel>> resp = new ResponseOutputModel<List<SomeModel>>();

    try
    {
        Tuple<List<SomeModel>, Boolean, String> asyncResp = await CacheProcessing.GetSomeModelList(GetApiKey()).ConfigureAwait(false);

        resp.Response = asyncResp.Item1;
        resp.Success = asyncResp.Item2;
        resp.Message = asyncResp.Item3;
    }
    catch (Exception ex)
    {
        LoggingHelper.Write(ex);
        resp.StatusCode = Enumerations.ApiResponseStatusCodes.ProcessingError;
        resp.Success = false;
        resp.Message = ex.Message;
    }

    return resp;
}

这会使重构复杂化,因为原始方法实际上具有bool successstring message 的输出参数;但似乎这可以使用Tuple&lt;&gt; 以体面和快速的方式完成;否则我们可以创建一个返回类型模型。

要正确地做到这一点,将有数百种重构方法;一项艰巨的任务。

  1. 这会按预期工作,并且是实现目标的最佳解决方案吗?
  2. 是否值得付出所有必要的努力,最终目标是提高 Web 服务器的可扩展性和随后的“性能”?

【问题讨论】:

    标签: c# asynchronous async-await ncache


    【解决方案1】:

    是否值得创建一个公开可等待任务返回类型的异步辅助方法?

    Nope.

    建议是创建如下的辅助方法

    这只是将内存中的工作排入线程池。

    (旁注:永远不应使用任务构造函数。永远。如果您需要将工作排队到线程池,请使用Task.Run 而不是带有Start 的任务构造函数)

    这会按预期工作,并且是实现目标的最佳解决方案吗?

    是否值得付出所有必要的努力,最终目标是提高 Web 服务器的可扩展性和随后的“性能”?

    这是同一个问题。目标是可扩展性;异步只是帮助您完成它的一种方法。

    异步有助于 ASP.NET 上的可伸缩性,因为它释放了可以处理其他请求的线程。但是,如果您使用另一个线程创建异步方法,那么这对您毫无帮助。我称之为“假异步”——这类方法看起来是异步的,但实际上它们只是在线程池上同步运行。

    与真正的异步相比,假异步实际上会损害您的可扩展性。

    【讨论】:

    • 有趣。我希望你能看到这个并发表评论;我之前正在阅读您的博客文章。所以到目前为止我看到的问题是 NCache 的Get 方法在等待网络流量的延迟以及来自服务器本身的请求的处理时会产生大量的等待时间。经过进一步审查,似乎我唯一可以异步/等待的真正项目将是对 ExecuteNonQueryAsync 的数据库调用;填充和返回数据表的方法似乎也没有足够的等待时间。
    • @Sivart:不,(非常旧的)数据表 API 不能很好地用于异步,即使 MS 确实尝试编写它们。较新的 EF 对异步更加友好。 NCache 最好的办法是向他们的开发人员提出问题。
    【解决方案2】:

    因为这是一个内存缓存,我这么说是出于对 NCache 的任何直接经验,但对其他缓存系统的经验。

    1. 是的,这当然可以。我宁愿选择一个允许我定义我的响应类型(如果可能的话)的响应结构或通用类。元组还不错,如果您选择类,可能会更好。但它们并不容易养眼。
    2. 现在,说到性能,有一个问题。这是为您准备的缓存服务器,您当然想要快速读写。访问内存缓存应该很容易。当您在这里直接访问内存时,从性能的角度来看,它并不会真正给您带来太多好处。您是否确保您的代码是完全异步的,并且您正在正确使用线程池以使事情顺利进行?是的,当然,但它所做的只是在您访问缓存时添加一个额外的层或工作。
    3. 当您要进行异步时,请确保您的内存缓存是线程安全的。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2020-06-25
      相关资源
      最近更新 更多