我使用以下部分解决了这个问题:
-
单个共享/命名内存区域。
-
-
一个名为信号量的每个进程,当被调用时,它在该进程中本地触发事件。
我的解决方案依赖于以下技巧:
-
共享信号量可以通过
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;
}
}
}