c# - C#异步调用同步服务调用的策略

标签 c# asynchronous service synchronization

业务逻辑封装在同步服务调用之后,例如:

interface IFooService
{
    Foo GetFooById(int id);
    int SaveFoo(Foo foo);
}

异步方式扩展/使用这些服务调用的最佳方式是什么?

目前我已经创建了一个简单的 AsyncUtils 类:

public static class AsyncUtils
{
    public static void Execute<T>(Func<T> asyncFunc)
    {
        Execute(asyncFunc, null, null);
    }

    public static void Execute<T>(Func<T> asyncFunc, Action<T> successCallback)
    {
        Execute(asyncFunc, successCallback, null);
    }

    public static void Execute<T>(Func<T> asyncFunc, Action<T> successCallback, Action<Exception> failureCallback)
    {
        ThreadPool.UnsafeQueueUserWorkItem(state => ExecuteAndHandleError(asyncFunc, successCallback, failureCallback), null);
    }

    private static void ExecuteAndHandleError<T>(Func<T> asyncFunc, Action<T> successCallback, Action<Exception> failureCallback)
    {
        try
        {
            T result = asyncFunc();
            if (successCallback != null)
            {
                successCallback(result);
            }
        }
        catch (Exception e)
        {
            if (failureCallback != null)
            {
                failureCallback(e);
            }
        }
    }
}

这让我可以异步调用任何东西:

AsyncUtils(
     () => _fooService.SaveFoo(foo),
     id => HandleFooSavedSuccessfully(id),
     ex => HandleFooSaveError(ex));

虽然这在简单的用例中有效,但如果其他进程需要协调结果,它很快就会变得棘手,例如,如果我需要在当前线程继续之前异步保存三个对象,那么我想要一种等待的方法-在/加入工作线程。

目前我想到的选项包括:

  • 让 AsyncUtils 返回一个 WaitHandle
  • 让 AsyncUtils 使用 AsyncMethodCaller 并返回 IAsyncResult
  • 重写 API 以包含开始、结束异步调用

例如类似于:

interface IFooService
{
    Foo GetFooById(int id);
    IAsyncResult BeginGetFooById(int id);
    Foo EndGetFooById(IAsyncResult result);
    int SaveFoo(Foo foo);
    IAsyncResult BeginSaveFoo(Foo foo);
    int EndSaveFoo(IAsyncResult result);
}

我应该考虑其他方法吗?每种方法的优点和潜在缺陷是什么?

理想情况下,我希望保持服务层简单/同步,并提供一些易于使用的实用方法来异步调用它们。我有兴趣了解适用于 C# 3.5 和 C# 4 的解决方案和想法(我们尚未升级,但会在不久的将来升级)。

期待您的想法。

最佳答案

如果您要求只保留 .NET 2.0,而不是在 3.5 或 4.0 上工作,这可能是最佳选择。

我对您当前的实现有三点意见。

  1. 您使用 ThreadPool.UnsafeQueueUserWorkItem 是否有特定原因? ?除非有特定原因,否则我建议使用 ThreadPool.QueueUserWorkItem相反,特别是如果您在一个大型开发团队中。 Unsafe 版本可能会在您丢失调用堆栈时出现安全漏洞,从而导致无法严格控制权限。

  2. 您当前的异常处理设计,使用 failureCallback , 将吞下所有异常,并且不提供任何反馈,除非定义了回调。如果您不打算正确处理它,传播异常并让它冒泡可能会更好。或者,您可以以某种方式将它推回调用线程,尽管这需要使用更像 IAsyncResult 的东西。 .

  3. 您目前无法判断异步调用是否已完成。这将是使用 IAsyncResult 的另一个优势。在您的设计中(尽管它确实增加了一些实现的复杂性)。


但是,一旦升级到 .NET 4,我建议只将它放在 Task 中。或 Task<T> ,因为它旨在非常干净地处理这个问题。而不是:

AsyncUtils(
     () => _fooService.SaveFoo(foo),
     id => HandleFooSavedSuccessfully(id),
     ex => HandleFooSaveError(ex));

您可以使用内置工具,只需编写:

var task = Task.Factory.StartNew( 
                () => return _fooService.SaveFoo(foo) );
task.ContinueWith( 
                t => HandleFooSavedSuccessfully(t.Result),
                    TaskContinuationOptions.NotOnFaulted);
task.ContinueWith( 
                t => try { t.Wait(); } catch( Exception e) { HandleFooSaveError(e); },
                    TaskContinuationOptions.OnlyOnFaulted );

诚然,最后一行有点奇怪,但这主要是因为我试图保留您现有的 API。如果你稍微修改一下,你可以简化它......

关于c# - C#异步调用同步服务调用的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3813849/

相关文章:

c# - 通过代码 session 工厂与 MySQL 设置 Nhibernate 映射

c# - Elmah 抛出 ObjectDisposedException

c# - NLog - 在多个数据库列中保存 ${message}

c# - 从同步方法调用异步方法

.net - 可以在异步回调中进行繁重的处理吗?

android - 将监听器设置为来自 Fragment 的 Android 服务

c# - SQL Server (2008) 将 ArrayList 或字符串传递给 SP 以进行 IN()

javascript - 将参数传递给 indexedDB 回调

html - 如何在 Angular 服务中创建html模板并将其用作htmlTemplateRef?

powershell - 尝试通过 Powershell/MSBuild 远程停止时找不到服务