代码之家  ›  专栏  ›  技术社区  ›  feihtthief

向.NET中的多个进程发送事件信号的最简单方法

  •  6
  • feihtthief  · 技术社区  · 16 年前

    4 回复  |  直到 16 年前
        1
  •  5
  •   Jim Mischel    16 年前

    您可以使用命名的EventWaitHandle。但是,在通知所有侦听进程之后,您还需要某种方法来重置事件。您可能可以执行一些操作,例如设置事件,然后在短时间后重置它:1秒或5秒。客户可能知道,事件不会连续这么快触发。

        2
  •  4
  •   grega g    16 年前

    命名信号量 命名互斥 用于进程间同步。

    Msdn 说:

    信号量有两种类型:本地信号量和命名系统信号量。如果使用接受名称的构造函数创建信号量对象,则该对象与该名称的操作系统信号量相关联。命名系统信号量在整个操作系统中可见,可用于同步进程的活动。

    Msdn

    希望这有帮助

        3
  •  2
  •   Franci Penov    16 年前

    • 你可以使用 信号量 由发布者设置,并让所有订阅者等待。但是,如果任何订户死亡,您的计数将被取消。您需要实现某种形式的僵尸检测

    • COM连接点 . 这需要对COM类和类型库进行管理员注册。

    对于松散耦合的发布者/订阅者模型(发布者对订阅者一无所知):

    • 文件或注册表更改侦听器

    • 你可以使用 COM+松散耦合事件 (通过 系统企业服务 ). 然而,由于LCE的复杂性,这对您来说可能是一种过度的杀伤力。

    • RegisterWindowMessage 到特定类的所有隐藏顶级窗口。所有订阅者都需要创建一个这样的窗口。这需要一些Win32互操作,但可能是实现松耦合发布服务器/订阅服务器的最轻量级方法。

        4
  •  1
  •   antiduh    7 年前

    我使用以下部分解决了这个问题:

    • 单个共享/命名内存区域。
    • 一个名为信号量的每个进程,当被调用时,它在该进程中本地触发事件。

    我的解决方案依赖于以下技巧:

    • 共享信号量可以通过 out createNew
    • 当一个共享内存区域被打开时,以前没有人持有它的句柄(你是第一个打开它的人),共享内存区域恰好被初始化为全零。谢谢Windows!

    • 共享内存区域存储已注册侦听器的数量以及每个侦听器的唯一ID。
    • 每个侦听器向线程池注册每个进程的锁,这样当它被弹出时,它们将在处理程序方法上得到回调。
    • 当一个进程想要触发所有进程中的事件时,他会查找共享内存区域中所有注册的ID,然后使用这些ID打开每个进程的信号量并弹出它们。
    • 如果,当我们打开其他人的共享信号量时,我们发现该信号量 out createdNew 参数为true时,我们知道进程在未注销自身的情况下崩溃,所以我们忽略它,然后自己立即注销它。

    一些代码:

        ...
        /// <remarks>
        /// ...
        /// The shared memory region that stores the registrations has the following structure:
        ///
        ///      +---- 4 Bytes ----+
        ///      |   NumListeners  |
        ///      +-----------------+
        ///      |   Listener ID   |
        ///      +-----------------+
        ///      |   Listener ID   |
        ///      +-----------------+
        ///      |       ...       |
        ///      +-----------------+
        ///
        /// ...
        /// </remarks>      
        public SharedEvent( string name, int maxListeners = 1024 )
        {
            this.Name = name;
            this.MaximumListeners = maxListeners;
    
            this.localWaitHandleId = -1;
    
            try
            {
                this.registrationLock = new Semaphore( 1, 1, RegistrationLockName() );
    
                this.registrations = MemoryMappedFile.CreateOrOpen(
                    RegistrationShmemName(),
                    4 + maxListeners * 4,
                    MemoryMappedFileAccess.ReadWrite,
                    MemoryMappedFileOptions.None,
                    null,
                    HandleInheritability.None
                );
    
                RegisterSelf();
            }
            catch
            {
                Dispose();
                throw;
            }
        }
    

    在所有注册人中触发事件的代码:

        public void Trigger(bool suppressSelfHandler = false)
        {
            bool modifiedList = false;
    
            // The finally block is a ConstrainedExecutionRegion... we definitely don't want to
            // deadlock the whole shared event because we crashed while holding the lock.
            RuntimeHelpers.PrepareConstrainedRegions();
            this.registrationLock.WaitOne();
            try
            {
                List<int> ids = ReadListenerIds();
    
                for( int i = 0; i < ids.Count; /* conditional increment */ )
                {
                    int memberId = ids[i];
    
                    if( suppressSelfHandler && memberId == this.localWaitHandleId )
                    {
                        i++;
                        continue;
                    }
    
                    Semaphore handle = null;
                    try
                    {
                        handle = GetListenerWaitHandle( memberId, false );
    
                        if( handle == null )
                        {
                            // The listener's wait handle is gone. This means that the listener died
                            // without unregistering themselves.
    
                            ids.RemoveAt( i );
                            modifiedList = true;
                        }
                        else
                        {
                            handle.Release();
                            i++;
                        }
                    }
                    finally
                    {
                        handle?.Dispose();
                    }
                }
    
                if( modifiedList )
                {
                    WriteListenerIds( ids );
                }
            }
            finally
            {
                this.registrationLock.Release();
            }
        }
    

    这显示了进程如何在系统中注册自身:

        private void RegisterSelf()
        {
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                this.registrationLock.WaitOne();
                List<int> ids = ReadListenerIds();
    
                if( ids.Count >= this.MaximumListeners )
                {
                    throw new InvalidOperationException(
                        "Cannot register self with SharedEvent - no more room in the shared memory's registration list. Increase 'maxSubscribers'."
                    );
                }
    
                this.localWaitHandleId = FindNextListenerId( ids );
    
                ids.Add( this.localWaitHandleId );
                ids.Sort();
    
                this.localWaitHandle = GetListenerWaitHandle( this.localWaitHandleId, true );
                this.localWaitHandleReg = ThreadPool.RegisterWaitForSingleObject(
                    this.localWaitHandle,
                    this.WaitHandleCallback,
                    null,
                    -1,
                    false
                );
    
                WriteListenerIds( ids );
            }
            finally
            {
                this.registrationLock.Release();
            }
        }
    
        private void UnregisterSelf()
        {
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                this.registrationLock.WaitOne();
    
                List<int> ids = ReadListenerIds();
    
                if( this.localWaitHandleId != -1 && ids.Contains( this.localWaitHandleId ) )
                {
                    ids.Remove( this.localWaitHandleId );
                }
    
                if( this.localWaitHandleReg != null )
                {
                    this.localWaitHandleReg.Unregister( this.localWaitHandle );
                    this.localWaitHandleReg = null;
                }
    
                if( this.localWaitHandle != null )
                {
                    this.localWaitHandle.Dispose();
                    this.localWaitHandle = null;
                }
    
                WriteListenerIds( ids );
            }
            finally
            {
                this.registrationLock.Release();
            }
        }
    

        private List<int> ReadListenerIds()
        {
            int numMembers;
            int[] memberIds;
            int position = 0;
    
            using( var view = this.registrations.CreateViewAccessor() )
            {
                numMembers = view.ReadInt32( position );
                position += 4;
    
                memberIds = new int[numMembers];
    
                view.ReadArray( position, memberIds, 0, numMembers );
                position += sizeof( int ) * numMembers;
            }
    
            return new List<int>( memberIds );
        }
    
        private void WriteListenerIds( List<int> listenerIds )
        {
            int position = 0;
    
            using( var view = this.registrations.CreateViewAccessor() )
            {
                view.Write( position, (int)listenerIds.Count );
                position += 4;
    
                foreach( int id in listenerIds )
                {
                    view.Write( position, (int)id );
                    position += 4;
                }
            }
        }