【发布时间】:2018-05-08 11:37:14
【问题描述】:
我们的在线商店有一个 ASP.Net MVC 应用程序。用户必须从多种付款方式中进行选择才能购买商品。为此,我们实现了一个抽象工厂模式:
public interface IPaymentServiceFactory
{
IPaymentService GetPaymentService(PaymentServiceEnum paymentServiceType);
}
public interface IPaymentService
{
PaymentSettingsModel GetPaymentSettingsModel();
}
在我们的Action中使用:
public ActionResult ProcessCart(PaymentDataModel paymentData)
{
var paymentService = _paymentServiceFactory.GetPaymentService(paymentData.PaymentServiceType);
var paymentSettings = paymentService.GetPaymentSettingsModel();
}
当我们了解某些支付方式需要内部异步调用时,就会出现问题。例如,第三方在线支付服务方法必须通过http异步调用,以便在他们身边创建支付对象。实现:
public class OnlinePaymentService : IPaymentService
{
private readonly IOnlinePaymentServiceApiClient _client;
public async Task<PaymentSettingsModel> GetPaymentSettings()
{
var result = await _client.CreatePaymentAsync();
return result;
}
}
所以我们提出了一个问题:如何处理不同支付方式的异步和同步场景。我们决定让一切异步。更新代码:
public interface IPaymentService
{
Task<PaymentSettingsModel> GetPaymentSettings();
}
public async Task<ActionResult> ProcessCart(PaymentDataModel paymentData)
{
var paymentService = _paymentServiceFactory.GetPaymentService(paymentData.PaymentServiceType);
var paymentSettings = await paymentService.GetPaymentSettingsModel();
}
到目前为止一切都很好,但要为所有其他支付方式实现这一点,我们不得不使用 Task.Run:
public class CashPaymentService : IPaymentService
{
public async Task<PaymentSettingsModel> GetPaymentSettings()
{
return await Task.Run(() => new PaymentSettingsModel());;
}
}
据我所知,这会创建两个不同的线程来处理 Action,这可能会导致 performance issue。 有没有办法避免这样的后果?在特定情况下使用 Task.Run 真的很糟糕吗?
【问题讨论】:
-
查看代码并比较
Task.Run、Task.FromResult和TaskCompletionSource。如果操作长时间运行,那么Task.Run是提供非阻塞实现的唯一选项,rest 两者都将最终阻塞,因为Task.FromResult的目的是返回硬编码/固定值,TaskCompletionSource是异步事件包装,它们都不适合用例 -
你发布了我引用的同一篇文章,它只是告诉你如何不使用
Task.Run,但它也建议,最后如何使用Task.Run进行异步包装,它只是警告不要在方法中使用它,因为这是 ASP.Net 应用程序的问题,而不是创建虚假的 Async 方法,而是从调用者本身包装它,在文章中Button Click。 -
@MrinalKamboj 在我的情况下 CashPaymentService.GetPaymentSettings() 不是长时间运行的操作。没有必要提高它。所以对我来说 Task.Run 在这种情况下真的是大材小用。此外,从 OOP 的角度来看,我不想更改 CashPaymentService 实现中的任何一行代码,因为当前的功能只是介绍新的支付方式。
-
@MrinalKamboj 从您的 cmets 中我无法理解的另一件事是关于使用 Task.Run 的包装方法。对于 mvc 应用程序和我的场景,我别无选择,只能将 Task.Run 放在 CashPaymentService 中。没有其他地方适合它。您在 GetPaymentSettingsTR 方法中为我所做的只是将 Task.Run 放入方法中,这正是您要避免的。
-
你所做的是正确的,这也是 Stephen Cleary 所推荐的,他建议反对的是,不要在
new PaymentSettingsModel()构造函数中放置Task.Run或创建一个假的 Async,因为它的令人困惑,但可以从调用者那里包装起来。到目前为止,构造函数调用几乎是硬编码值,即使Task.FromResult或TaskCompletionSource也可以工作,但如果引入了延迟(如我所示),那么你会发现只有Task.Run是非阻塞调用,休息都会阻塞
标签: c# asp.net-mvc design-patterns async-await abstract-factory