通常我们见到的单件模式的实现只限于单个执行过程.然而如果要求某个公共对象对多个程序实例,甚至是不同程序都只能有一个实例,该怎么办呢?
    首先,我们想到这个对象一定是跨进程,跨应用程序域的.应用程序域是系统内程序执行的安全和资源边界,不同的应用程序域其内存都是独立的,使用普通的方式,是不可能实现的.
    要跨越应用程序域,我们似乎只有一个选择,使用Remoting技术,使不同的程序访问同一个远程对象。
    在如何实现上,首先想到的是做一个Windows服务,让它一直保持运行状态,将唯一的公共对象实例保存在其中,其他程序通过这个服务获取对象。但是这样无疑增大了实现的复杂度,似乎并不必要,于是放弃。
    权衡之后,决定使用一个比较轻量级的方案, 首先,创建一个公共对象的宿主程序,客户程序获取公共对象实例时,首先检查这个宿主程序是否已经启动,如果没有,则创建一个进程,启动宿主程序。如果已经存在,则通过IPC信道(选用IPC信道是考虑到其效率比较高),获取宿主程序公开的远程对象。
    实现过程如下:
首先创建宿主程序,大数学家华罗庚说,把问题简化到不能再简单而又不不失重要性,便是成功的第一步.(n年前看到的,无法考证),于是我们的这个公共类也就只有一个属性。注意,一定要继承自MarshalByRefObject,否则,你会发现获取的其实只是一个本地对象,这个可不能简化:)
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;


namespace OSSingleton.OSSHost
{
    
//需要实现单件模式的对象
    public class TheOne : MarshalByRefObject
    
{
        
private static object syncObj = new object();
        
private string name = string.Empty;

        
public string Name
        
{
            
get
            
{
                
lock (syncObj)
                
{
                    
return name;
                }

            }


            
set
            
{
                
lock (syncObj)
                
{
                    name 
= value;
                }

            }

        }

    }


    
class Program
    
{
        
static void Main(string[] args)
        
{
            
//另启一个线程注册远程信道
            Thread thread = new Thread(RegServChannel);
            thread.Start();

            
//阻塞当前线程,使程序不会立即结束
            Thread.CurrentThread.Join();
        }


        
private static void RegServChannel()
        
{
            
try
            
{
                
//注册IPC信道
                IpcServerChannel channel = new IpcServerChannel("OSSHostOFOSSingleton""OSSingleton");
                ChannelServices.RegisterChannel(channel, 
false);

                
//注册需要公开的对象
                RemotingConfiguration.RegisterWellKnownServiceType(
                    
typeof(TheOne), "TheOne", WellKnownObjectMode.Singleton);
            }

            
catch (Exception ex)
            
{
                
//若注册过程中出现错误,则显示错误并终止程序。
                MessageBox.Show(ex.Message);
                Process.GetCurrentProcess().Kill();
            }

        }

    }

}




    接下来,是一个DLL,提供了对获取远程对象的一个封装。
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels;
using System.Threading;
using OSSingleton.OSSHost;
using System.Diagnostics;

namespace OSSingleton
{
    
public static class OneInOS
    
{
        
private static TheOne theOne;

        
public static TheOne GetInstance()
        
{
            
if (theOne == null)
            
{
                
//判断是否已经启动了单件宿主进程
                if (!Array.Exists<Process>(Process.GetProcesses(),
                    
delegate(Process p)
                    
{
                        
return p.ProcessName == "OSSHost";
                    }
))
                
{
                    StartHostExe();
                    
//等待一小段时间,使宿主进程完成启动
                    Thread.CurrentThread.Join(2000);
                }

                
//获取远程对象
                theOne = (TheOne)Activator.GetObject(typeof(TheOne), @"ipc://OSSingleton/TheOne");
            }

            
return theOne;
        }


        
/// <summary>
        
/// 启动单件宿主进程
        
/// </summary>

        private static void StartHostExe()
        
{
            Process p 
= new Process();
            p.StartInfo.FileName 
= @"..\..\..\OSSHost\bin\Debug\OSSHost.exe";
            p.StartInfo.CreateNoWindow 
= true;
            p.StartInfo.UseShellExecute 
= false;
            p.Start();
        }

    }

}


    最后是一个测试程序,功能很简单:获取对象,使其属性Name末尾加上一个星号,然后打印。这样我们每次启动程序都能看到输出多了一个星号,也就证明了,我们的实例确实只有一个。
using System;
using System.Collections.Generic;
using System.Text;
using OSSingleton;
using OSSingleton.OSSHost;

namespace OSSingletonTest
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            TheOne theOne 
= OneInOS.GetInstance();
            theOne.Name 
= theOne.Name + "*";
            Console.WriteLine(theOne.Name);
        }

    }

}

在这里下载全部源码

    现在,我们使用的IPC信道(IPC信道是.NET 2.0新增的),虽然提高了系统性能,但是也被限制在了一个OS内,如果使用TCP/HTTP信道,并固定一台计算机专门运行宿主程序。那么就可以在任意范围内实现单件模式。

posted on 2007-12-17 14:10  yujiasw  阅读(643)  评论(0编辑  收藏  举报