在TPL任务中包装.NET Remoting异步方法

时间:2018-11-05 12:45:53

标签: c# task-parallel-library remoting

我们有一个基于.NET Remoting的旧版应用程序。我们的客户端客户端库目前仅支持同步操作。我想使用基于TPL的async Task<>方法添加异步操作。

作为概念证明,我已经建立了基于these instructions修改版的基本远程服务器/客户端解决方案。

我还发现this article描述了如何将基于APM的异步操作转换为基于TPL的异步任务(使用Task.Factory.FromAsync

我不确定的是我是否被迫在.BeginInvoke()中指定回调函数并且也被指定.EndInvoke()。如果两者都需要,则回调函数和.EndInvoke()之间的区别到底是什么。如果只需要一个,则应使用哪一个返回值,并确保我have no memory leaks

这是我当前的代码,其中我没有将回调传递给.BeginInvoke()

public class Client : MarshalByRefObject
{
    private IServiceClass service;

    public delegate double TimeConsumingCallDelegate();

    public void Configure()
    {
        RemotingConfiguration.Configure("client.exe.config", false);

        var wellKnownClientTypeEntry = RemotingConfiguration.GetRegisteredWellKnownClientTypes()
            .Single(wct => wct.ObjectType.Equals(typeof(IServiceClass)));

        this.service = Activator.GetObject(typeof(IServiceClass), wellKnownClientTypeEntry.ObjectUrl) as IServiceClass;
    }

    public async Task<double> RemoteTimeConsumingRemoteCall()
    {
        var timeConsumingCallDelegate = new TimeConsumingCallDelegate(service.TimeConsumingRemoteCall);

        return await Task.Factory.FromAsync
            (
                timeConsumingCallDelegate.BeginInvoke(null, null),
                timeConsumingCallDelegate.EndInvoke
           );
    }

    public async Task RunAsync()
    {
        var result = await RemoteTimeConsumingRemoteCall();
        Console.WriteLine($"Result of TPL remote call: {result} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
    }
}

public class Program
{
    public static async Task Main(string[] Args)
    {
        Client clientApp = new Client();
        clientApp.Configure();

        await clientApp.RunAsync();

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey(false);
    }
}

2 个答案:

答案 0 :(得分:1)

回调函数和.EndInvoke()之间的区别在于,回调将在线程池的任意线程上执行。如果必须确保从与调用BeginInvoke相同的线程上的读取中获得结果,那么您不应使用回调,而应轮询IAsyncResult对象并在操作完成后调用.EndInvoke()。 / p>

如果在.EndInvoke()之后立即调用.Beginnvoke(),则会阻塞线程,直到操作完成。这会起作用,但扩展性很差。

所以,您的工作似乎没事!

答案 1 :(得分:0)

出于效率考虑,必须指定回调函数。如果FromAsync仅可使用IAsyncResult,则异步结果完成后将无法通知它。它必须使用事件来等待。这会阻塞线程(或者,它注册一个线程池等待,也许还不错)。

高效的异步IO需要某种程度的回调。

如果您要异步,那么我认为您正在执行此操作,因为您有许多并发呼叫,或者这些呼叫运行时间很长。因此,您应该使用更有效的机制。

如果您没有很多电话或长时间运行的电话,那么异步不会以任何方式帮助您提高性能,但是它仍然可能使GUI编程更容易。

所以这是不正确的:

public async Task<double> RemoteTimeConsumingRemoteCall()
{
    var timeConsumingCallDelegate = new TimeConsumingCallDelegate(service.TimeConsumingRemoteCall);

    return await Task.Factory.FromAsync
        (
            timeConsumingCallDelegate.BeginInvoke(null, null),
            timeConsumingCallDelegate.EndInvoke
       );
}

要确保您的实现确实不会阻塞任何线程,我可以这样做:在服务器端插入Thread.Sleep(100000),并在客户端发出1000个并发调用。您应该发现线程数没有增加。