在 TPL 任务中包装 .NET Remoting 异步方法
Wrap .NET Remoting async method in TPL Task
我们有一个遗留的基于 .NET Remoting 的应用程序。我们的客户端客户端库目前仅支持同步操作。我想使用基于 TPL async Task<>
方法添加异步操作。
作为概念证明,我已经建立了一个基于 these instructions 修改版本的基本远程处理 server/client 解决方案。
我还发现 this article 描述了如何将基于 APM 的异步操作转换为基于 TPL 的异步任务(使用 Task.Factory.FromAsync
)
我不确定我是否必须在 .BeginInvoke()
中指定回调函数并指定 .EndInvoke()
。如果两者都需要,那么回调函数和.EndInvoke()
的区别到底是什么?如果只需要一个,我应该使用哪个 return 值并确保我 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);
}
}
出于效率原因,您必须指定回调函数。如果 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 个并发调用。你应该会发现线程数并没有增加。
回调函数与.EndInvoke()
的区别在于回调函数将在线程池中的任意线程上执行。如果您必须确保从与调用 BeginInvoke 的线程相同的线程上读取结果,则不应使用回调,而应轮询 IAsyncResult 对象并在操作完成时调用 .EndInvoke()
。
如果您在 .Beginnvoke()
之后立即调用 .EndInvoke()
,您将阻塞线程直到操作完成。这会起作用,但扩展性很差。
所以,你做的似乎还不错!
我们有一个遗留的基于 .NET Remoting 的应用程序。我们的客户端客户端库目前仅支持同步操作。我想使用基于 TPL async Task<>
方法添加异步操作。
作为概念证明,我已经建立了一个基于 these instructions 修改版本的基本远程处理 server/client 解决方案。
我还发现 this article 描述了如何将基于 APM 的异步操作转换为基于 TPL 的异步任务(使用 Task.Factory.FromAsync
)
我不确定我是否必须在 .BeginInvoke()
中指定回调函数并指定 .EndInvoke()
。如果两者都需要,那么回调函数和.EndInvoke()
的区别到底是什么?如果只需要一个,我应该使用哪个 return 值并确保我 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);
}
}
出于效率原因,您必须指定回调函数。如果 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 个并发调用。你应该会发现线程数并没有增加。
回调函数与.EndInvoke()
的区别在于回调函数将在线程池中的任意线程上执行。如果您必须确保从与调用 BeginInvoke 的线程相同的线程上读取结果,则不应使用回调,而应轮询 IAsyncResult 对象并在操作完成时调用 .EndInvoke()
。
如果您在 .Beginnvoke()
之后立即调用 .EndInvoke()
,您将阻塞线程直到操作完成。这会起作用,但扩展性很差。
所以,你做的似乎还不错!