在上一篇文章中([原创]我所理解的Remoting(2):远程对象生命周期的管理—Part I ),我简要的讲述了CLR的垃圾回收机制和Remoting 基于Lease的对象生命周期的管理。在这篇文章中,我们将以此为基础,继续我们的话题。在文章的开始,我将以我的理解详细地讲述Remoting中两个重要的概念——Lease和Sponsorship。然后我通过一个Sample,为大家演示如何以不同的方法延长远程对象的生命周期。
我们先不谈远程对象、本地对象。 不管是远程的对象,还是本地对象,都对于程序Application Domain 所在地址空间的一块连续的托管堆(managed heap)内存区域。在.NET环境下,其对象的生命周期,也就是对应托管堆的回收,都由垃圾回收器(garbage collector)负责。当需要进行垃圾回收时(如果不是强制进行垃圾回收,和程序卸载,垃圾回收操作一般出现在第0代对象充满的时候),垃圾回收器扫描托管堆,标记垃圾对象(实际上是标记非垃圾对象,未被标记的则是垃圾对象),并最终回收垃圾对象。
通过前面章节的介绍,我们知道了,CLR通过当前程序运行时的一个根(root)的列表来判断一个对象是否是垃圾对象——没有被根直接或者间接引用的对象则是垃圾对象。从另一个角度讲,如果想让一个对象存活,或者你试图让一个对象具有更长的生命周期,那么不就必须使它被一个根直接或者间接引用——比如你可以使用一个全局变量引用它,那么在这个全局变量的生命周期内,这个对象就会一直存活;你甚至可以让一个静态变量引用它,那么这个对象将永远不会被垃圾回收,直到所在的AppDomain被卸载。而我们现在来讨论Remoting 中远程对象生命周期的管理,说白了,其本质就是在Remoting Framework中,如何创建一些具有根性质的对象引用创建的远程对象(相对于Client端来讲),从而适当地(我们不能让远程对象具有太长的生命周期,那样会见中内存的压力,同样我们也不能使远程对象,那样会造成频繁的对象的频繁创建进而影响系统的性能)阻止垃圾回收器回收该对象。
那么这个引用远程对象的对象是谁呢?它就是我们要讲的Lease。当Client端的Proxy通过Marshal传到Host环境的时候,Remoting Framework 激活对应的远程对象。与此同时,Lease Manager(整个机遇Lease生命周期管理的总负责)会为该远程对象创建对应的Lease,从垃圾回收的角度讲,远程对象有了Lease对象的引用,则可以在垃圾收器的铡刀下得以幸存。但是通过前面的分析,远程对象不能老是存活者,他是具有一定的生命周期的,也就是说,一旦到了该寿终正寝的时候,垃圾回收器就该对他动刀子。而且,这个生命周期应该是可以配置的,系统地设计人员根据具体的程序运作状况,计算出一个合理的生命周期,在部署的时候,通过配置文件为之设定。
那么这样的机制又是如何实现的呢?到现在为止我们知道,远程对象存在的唯一条件就是它的Lease存在,Lease一旦垃圾回收了,那么它的死期也不远了。这样我们就可以通过Lease对象的生命周期来间接地控制远程对象的生命周期。而Lease对象的生命周期是可以配置的。那么现在我们可以把我们的关注点放在如果控制Lease的生命周期上来。在Remoting中,Lease是实现System.Runtime.Remoting.Lifetime. ILease的类的对象。
namespace
System.Runtime.Remoting.Lifetime
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
// Summary:
// Defines a lifetime lease object that is used by the remoting lifetime service.
[ComVisible(true)]
public interface ILease
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
LeaseState CurrentState
{ get; }
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
TimeSpan InitialLeaseTime
{ get; set; }
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
TimeSpan RenewOnCallTime
{ get; set; }
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
TimeSpan SponsorshipTimeout
{ get; set; }
void Register(ISponsor obj);
void Register(ISponsor obj, TimeSpan renewalTime);
TimeSpan Renew(TimeSpan renewalTime);
void Unregister(ISponsor obj);
}
}
了解在托管环境下Lease是怎样一个对象之后,我们来看看,在Remoting中Lease的生命周期是如果决定的。在前面一节我们提到过我们有3种方式来设置Lease 的各个属性(初始的时间:InitialLeaseTime,一个远程调用所能延续的时间:RenewOnCallTime,Lease Manager联系对应的Sponsor的时间:SponsorshipTimeout)——通过Configuration;通过设置LifetimeServices的静态属性(LeaseTime,RenewOnCallTime,LeaseManagerPollTime,SponsorshipTimeout);同过Override MarshalByRefObj的InitializeLifetimeService。当Lease对象创建之后,Lease Manager会为Lease设置通过上面方式设定的属性。随后Lease Manager会每隔一定的时间(由LeaseManagerPollTime设定)轮询每个Lease,查看Lease是否过期;随着时间的推移,Lease的租期(InitialLeaseTime - Expired time)越来越少。在这期间,Clien端和Server端获得该Lease,调用Renew方法来延长Lease的租期;此外,来自Client端的远程调用也会把Lease的生命周期延长至一个设定的时间(由RenewOnCallTime设定)。
注:只有在Lease的生命周期小于由RenewOnCallTime设定的时间的条件下,远程调用才会对Lease的租期起作用,或者这样说:current lease time = MAX(lease time - expired time,renew on call time)
那么当Lease Manager在获知某个Lease的已经过期?他会做什么样的操作呢?它会马上结束该Lease吗?就像我在上一章所举的租房的例子一样,房东在房租到期之后,出于人性化的考虑,他会首先通知承租人是否有续租的意愿。如果有,可以续约。在Remoting中也存在这样一种状况,Client可以在Lease尚未到期的时候,为他注册一个或多个Sponsor,Lease Manger会首先联系注册到该Lease的Sponsor,如果获得这样的Sponsor,则调用Sponsor的Renewal,从而实现续约的目的。由于Sponsor处于Client端所在的Context,Lease Manager调用Sponsor实际上是一种远程调用,由于远程调用的不确定性,必须设定Lease Manager联系Sponsor的时间范围(由SponsorshipTimeout属性设定),如果超出这样的范围,则认为Sponsor不可得。
在Remoting中,一个Sponsor是一个是实现了System.Runtime.Remoting.Lifetime. ISponsor Interface的类对象。该接口只有一个成员方法:Renewal。还有一点需要特别说明的是,Spnosor是被设计来被处于Server端的Lease Manager调用的。由于这是一个跨AppDomain的调用,我们知道由于AppDomain的隔离性,在一个AppDomain创建的对象不能在另一个Appdomain中直接调用,需要经过一个Marshal的过程——Marshal By Refence 或者 Marshal By Value。我们一般采用Marshal By Refence的方式,我们经常使用的System.Runtime.Remoting.Lifetime.ClientSponsor就是直接继承自System. MarshalByRefObject。
namespace
System.Runtime.Remoting.Lifetime
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
public interface ISponsor
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
TimeSpan Renewal(ILease lease);
}
}
一旦Lease过期,在既定的时间内,不能获得对应的Sponsor(或者是Sponsor 的Renewal方法返回TimeSpan.Zero),那么Lease Manager就把该Lease标记为过期。如果这时有一个远程调用,Remoting Framework会通过Lease Manager得知Lease已经过期,如果Client Activation模式直接会抛出异常;如果是Singleton 模式的Server Activation,则会创建一个新的对象,原来对象的状态将不复存在。这里有一点需要特别注意的是,Lease Manager就把该Lease标记为过期,并不等于该Lease马上会被垃圾回收掉,同理,这时候虽然远程对象可能还是存在的,由于这时候我们不能保证调用的安全性——不能确定该对象什么时候被垃圾回收,对于远程调用来说,它已经没有任何意义。
从上面整个流程来看,为了保持远程对象,我们有Lease对象;为了保持Lease对象,我们有Sponsor对象,那么什么对象来保持Sponsor对象呢?那就要依赖于我们的Client代码了。如果注册的Sponsor对象如果一直不被回收的话,远程对象将永远存在。所以我们应该根据实际的需要,取消Sponsor对Lease的注册。
下面我们照例来展示一个Sample,在这个Sample中,我们设计一个计数器,获得某个对象某个方法的调用次数。
Step 1 :整个Solution的构架(Artech.LifetimeManagement.RemoteService被Artech.LifetimeManagement.Client和Artech.LifetimeManagement.Hosting引用)
![](https://i-blog.csdnimg.cn/blog_migrate/43016ea7397dd02f827ff696d219cb92.jpeg)
Step 2:远程对象Artech.LifetimeManagement.RemoteService/CounterService.cs
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Remoting.Lifetime;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
namespace
Artech.LifetimeManagement.RemoteService
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
public class CounterService:MarshalByRefObject
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
private int _count;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
public int GetCount()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
this._count++;
return this._count;
}
public CounterService()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("Counter Object has been activated!");
}
~CounterService()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("Counter Object has been destroied!");
}
public override object InitializeLifetimeService()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
lease.InitialLeaseTime = TimeSpan.FromSeconds(1);
lease.RenewOnCallTime = TimeSpan.FromSeconds(1);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(1);
}
return lease;
}
}
}
为了确定对象的创建和回收,我们定义了Constructor和重写了 Finalize方法。GetCouter是 远程调用的方法,每次调用,技术器一次递增并返回该计数。为了更容易地演示对象的生命周期,我们重写了InitializeLifetimeService,设置了一些列短时间的Lease属性(都为1s)。
Step 3 Host : Artech.LifetimeManagement.Hosting
App.config
<?
xml version="1.0" encoding="utf-8"
?>
<
configuration
>
<
system
.runtime.remoting
>
<
application
name
="Artech.MyRemoting"
>
<
service
>
<
wellknown
type
="Artech.LifetimeManagement.RemoteService.CounterService,Artech.LifetimeManagement.RemoteService"
mode
="Singleton"
objectUri
="Counter.rem"
></
wellknown
>
<
activated
type
="Artech.LifetimeManagement.RemoteService.CounterService,Artech.LifetimeManagement.RemoteService"
></
activated
>
</
service
>
<
channels
>
<
channel
type
="System.Runtime.Remoting.Channels.Http.HttpChannel,System.Runtime.Remoting, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
port
="1234"
>
<
serverProviders
>
<
provider
ref
="wsdl"
/>
<
formatter
ref
="binary"
typeFilterLevel
="Full"
/>
</
serverProviders
>
<
clientProviders
>
<
formatter
ref
="binary"
/>
</
clientProviders
>
</
channel
>
</
channels
>
</
application
>
</
system.runtime.remoting
>
</
configuration
>
我定义了两个Service,一个WellKnown Service,一个CAO Service。因为我们将同时比较两种不同激活方式的生命周期的管理。在前面我们不止一次地说,调用Sponsor是一种远程调用,说得更确切地,只一种远程回调(Remote Callback),所以我们要把Type Filter Level设为Full,其原因可以参考我们文章([原创].NET Remoting: 如何通过Remoting实现双向通信(Bidirectional Communication)),在这里就不再说明。
Program.cs
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Threading;
using
System.Runtime.Remoting;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
namespace
Artech.LifetimeManagement.Hosting
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
class Program
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
static void Main(string[] args)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
RemotingConfiguration.Configure("Artech.LifetimeManagement.Hosting.exe.config", false);
Console.WriteLine("Calculator has begun to listen
");
GarbageCollect();
Console.Read();
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void GarbageCollect()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Thread.Sleep(10000);
GC.Collect();
}
}
}
}
通过上面的Code,我们先注册App.config的配置,为了更加清楚地看清对象的回收时间,我们每隔10s作一次垃圾回收。
Step 4 Client:Artech.LifetimeManagement.Client
App.config
<
configuration
>
<
system
.runtime.remoting
>
<
application
>
<
channels
>
<
channel
ref
="http"
port
="0"
>
<
clientProviders
>
<
formatter
ref
="binary"
/>
</
clientProviders
>
<
serverProviders
>
<
formatter
ref
="binary"
typeFilterLevel
="Full"
/>
</
serverProviders
>
</
channel
>
</
channels
>
</
application
>
</
system.runtime.remoting
>
</
configuration
>
由于处于Server端的Lease Manager要CallbackClient端的Sponsor,Client端必须注册一个Channel用于回调。同样把Type Filter Level设为Full。
Program.cs
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Remoting;
using
Artech.LifetimeManagement.RemoteService;
using
System.Threading;
using
System.Runtime.Remoting.Lifetime;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
namespace
Artech.LifetimeManagement.Client
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
class Program
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
const int _invocationFrequency = 4;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void Main(string[] args)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
RemotingConfiguration.Configure("Artech.LifetimeManagement.Client.exe.config", false);
RemotingConfiguration.RegisterActivatedClientType(typeof(CounterService), "http://localhost:1234/Artech.MyRemoting");
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
CounterService singletonCounter = Activator.GetObject(typeof(CounterService),"http://localhost:1234/Artech.MyRemoting/Counter.rem")
as CounterService;
CounterService caoCounter = new CounterService();
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
Thread singletonThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
Thread caoThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
singletonThread.Name = "Singleton";
caoThread.Name = "CAO";
singletonThread.Start(singletonCounter);
caoThread.Start(caoCounter);
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void InvocateCounterService(object counter)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
CounterService counterService = counter as CounterService;
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
try
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("{1}: The count is {0}", counterService.GetCount(), Thread.CurrentThread.Name.PadRight(10));
Thread.Sleep(_invocationFrequency * 1000);
}
catch (Exception ex)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
if (Thread.CurrentThread.Name == "Singleton")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate Singleton counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
if (Thread.CurrentThread.Name == "CAO")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate CAO counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
}
}
}
}
}
通过上上面的Code,我首先创建了连个Proxy,一个Singleton和一个CAO。然后创建连个线程,并在这两个线程中以固定的时间间隔(4s:_ invocationFrequency = 4)通过这两个Proxy进行远程调用。
现在我们来看看运行的结果:
启动Hosting:
![](https://i-blog.csdnimg.cn/blog_migrate/0c41480c37834d569f2d4e7ffd9e5daa.jpeg)
启动Client,等待一段时间:
![](https://i-blog.csdnimg.cn/blog_migrate/3676a9cd29bc4e33a8c14be4efc30eb5.jpeg)
再来看Host现在的显示:
![](https://i-blog.csdnimg.cn/blog_migrate/d9ea35362c79adb74837bd19a1c79623.jpeg)
我们可以看到,对于Singleton Proxy,调用没有出现异常,但是调用的计数器不没有维持一个持续的增长——从1到3,然后又从1-3,这样周而复始,这就证明了,没3次调用的远程对象不是同一个,当Lease过期之后,一个新的远程对象被创建。从Host 的输出也验证了这点,远程对象在不断地被创建。还有一个有意思的是,调用了3次Constructor之后才开始调用Finalizer方法,这说明了什么呢?这说明了,Lease过期后的调用,会导致新的远程对象的创建,但实际上这是,该远程对象还没有被回收。它在连续创建了3个新的对象后,才真正被垃圾回收。
而对于CAO Proxy,则不同,在第4次调用时,出现Exception,这Lease过期,再调用某个远程方法,会直接抛出Exception。
1.通过远程调用来延长远程对象的生命周期
通过我们开始的分析,在Lease的租约小于renew on call time,远程调用会使租约延长。按照这样理论,如果我们提高远程调用的频率,我们可以延长远程对象的生命周期。基于这样的想法,我们把效用的时间间隔从原来的4缩短为1s(_ invocationFrequency = 1)。再次运行。
Client端:
![](https://i-blog.csdnimg.cn/blog_migrate/7fc50c00f46e94f62ca195856cea976e.jpeg)
Host:
![](https://i-blog.csdnimg.cn/blog_migrate/d19b04b5c4f6645c737ea76fa6a31084.jpeg)
正如我们想得一样,无论是对于Singleton Proxy还是CAO Proxy,计数器一直维持一个持续增加的趋势,并且没有Exception抛出。从Client端就可以看出,Singleton Proxy和CAO Proxy调用的始终是一个远程对象,而Host的输出更是确凿地证明了,从始到终,只有连个对象被创建,一个对于Singleton,另一个对于CAO。
2.通过Lease来延长生命周期
上面我们通过远程调用来延长远程对象的生命周期,现在我们采用另一种方法,直接利用Lease对象来延长远程对象的生命周期。我们改动Client的代码:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Remoting;
using
Artech.LifetimeManagement.RemoteService;
using
System.Threading;
using
System.Runtime.Remoting.Lifetime;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
namespace
Artech.LifetimeManagement.Client
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
class Program
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
const int _invocationFrequency = 4;
const int _leaseRenewalFrequency = 1;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void Main(string[] args)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
RemotingConfiguration.Configure("Artech.LifetimeManagement.Client.exe.config", false);
RemotingConfiguration.RegisterActivatedClientType(typeof(CounterService), "http://localhost:1234/Artech.MyRemoting");
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
CounterService singletonCounter = Activator.GetObject(typeof(CounterService),"http://localhost:1234/Artech.MyRemoting/Counter.rem")
as CounterService;
CounterService caoCounter = new CounterService();
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
Thread singletonThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
Thread caoThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
Thread singletonLeaseRennewal = new Thread(new ParameterizedThreadStart(ExtendLifetimeViaLease));
Thread caoLeaseRenewal = new Thread(new ParameterizedThreadStart(ExtendLifetimeViaLease));
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
singletonThread.Name = "Singleton";
caoThread.Name = "CAO";
singletonThread.Start(singletonCounter);
caoThread.Start(caoCounter);
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
singletonLeaseRennewal.Start(singletonCounter);
caoLeaseRenewal.Start(caoCounter);
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void ExtendLifetimeViaLease(object counter)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
CounterService counterService = counter as CounterService;
ILease lease = (ILease)RemotingServices.GetLifetimeService(counterService);
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
if (lease == null)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("Can not retrieve the lease!");
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
lease.Renew(TimeSpan.FromSeconds(_leaseRenewalFrequency));
Thread.Sleep(_leaseRenewalFrequency);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void InvocateCounterService(object counter)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
CounterService counterService = counter as CounterService;
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
try
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("{1}: The count is {0}", counterService.GetCount(), Thread.CurrentThread.Name.PadRight(10));
Thread.Sleep(_invocationFrequency * 1000);
}
catch (Exception ex)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
if (Thread.CurrentThread.Name == "Singleton")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate Singleton counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
if (Thread.CurrentThread.Name == "CAO")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate CAO counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
}
}
}
}
}
在上面的代码中,我通过ExtendLifetimeViaLease方法每个一定的时间(1s:_leaseRenewalFrequency = 1)对获得的Lease Renew 一次。
static
void
ExtendLifetimeViaLease(
object
counter)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
CounterService counterService = counter as CounterService;
ILease lease = (ILease)RemotingServices.GetLifetimeService(counterService);
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
if (lease == null)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("Can not retrieve the lease!");
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
lease.Renew(TimeSpan.FromSeconds(_leaseRenewalFrequency));
Thread.Sleep(_leaseRenewalFrequency);
}
}
象原来的例子一样,分别在连个线程中以一定时间间隔(4s)调用远程对象,不过这次我们创建两个新的线程不同对Lease进行Renew,这样确保Lease用不过期。实验证明,输出结果和上面完全一样。
3.通过Sponsor来延长生命周期
不说废话直接来看代码:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.Remoting;
using
Artech.LifetimeManagement.RemoteService;
using
System.Threading;
using
System.Runtime.Remoting.Lifetime;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
namespace
Artech.LifetimeManagement.Client
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
class Program
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
const int _invocationFrequency = 4;
static ISponsor _singletonSponsor;
static ISponsor _caoSponsor;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void Main(string[] args)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
RemotingConfiguration.Configure("Artech.LifetimeManagement.Client.exe.config", false);
RemotingConfiguration.RegisterActivatedClientType(typeof(CounterService), "http://localhost:1234/Artech.MyRemoting");
CounterService singletonCounter = Activator.GetObject(typeof(CounterService),"http://localhost:1234/Artech.MyRemoting/Counter.rem")
as CounterService;
CounterService caoCounter = new CounterService();
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
Thread singletonThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
Thread caoThread = new Thread(new ParameterizedThreadStart(InvocateCounterService));
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
singletonThread.Name = "Singleton";
caoThread.Name = "CAO";
singletonThread.Start(singletonCounter);
caoThread.Start(caoCounter);
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
_singletonSponsor = ExtendLifetimeViaSponsor(singletonCounter);
_caoSponsor = ExtendLifetimeViaSponsor(caoCounter);
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static void InvocateCounterService(object counter)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
CounterService counterService = counter as CounterService;
while (true)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
try
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine("{1}: The count is {0}", counterService.GetCount(), Thread.CurrentThread.Name.PadRight(10));
Thread.Sleep(_invocationFrequency * 1000);
}
catch (Exception ex)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
if (Thread.CurrentThread.Name == "Singleton")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate Singleton counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
if (Thread.CurrentThread.Name == "CAO")
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
Console.WriteLine("Fail to invocate CAO counter because /"
{0}/"", ex.Message);
break;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
static ISponsor ExtendLifetimeViaSponsor(CounterService counter)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
CounterService counterService = counter as CounterService;
ILease lease = (ILease)RemotingServices.GetLifetimeService(counterService);
ClientSponsor sponsor = new ClientSponsor(TimeSpan.FromSeconds(4));
sponsor.Register(counterService);
return sponsor;
}
}
}
上面的代码中,我通过ExtendLifetimeViaSponsor方法为Lease注册一个Sposnor,并把Renew时间设为4s,最后把该Sposnor负值给一个静态变量,这样他不会被垃圾回收。那么每次Lease Manager获得该Sponsor时候,会自动把Lease 的租期变为4s。这样远程对象将会永久存活。可以想象,输出结果将会和上面一样。
相关章节:
[原创]我所理解的Remoting(3):创建CAO Service Factory使接口和实现相互分离