我主要使用.NET进行编程,喜欢它的异步/并发原语,如Tasks、ResetEvents等。今天第一次修改C++程序,并理解了整个构建过程(我更新了LigthningDB.NET项目至0.9.14)。但我仍然缺乏C++知识。
我想为LMDB项目添加一个新功能(以满足自己的需求),即通知系统(类似于Redis):
- 我希望有一个光标返回一个可等待对象,在其表中每次发生数据更改时发出信号。
- 我想获取一些数据与信号一起(指向数据结构的指针),例如键或键+值。
- 这个对象将与光标一起处理,是光标的一部分,但它会在光标引用的DB中发出更改信号。
- 这必须在跨平台运行,并且可以使用任何脏的Windows特定方法来完成任务。
从C++中可以使用
System.Threading
原语,并且我知道如何使用WaitHandle进行阻塞调用。有没有一种方法使其异步?有一系列关于异步同步原语的精彩文章,但它们使用的是TaskCompletionSource
,而这仅适用于.NET内部。是否可能为本机互操作创建类似的解决方案?一种解决方案可能是命名管道或套接字,将更改传输到一个侦听器/调度程序(Redis或Rhino.Queues样式),但性能会受到影响:写操作将不得不分配、复制和推送数据,并且数据将不得不传输-比传递指向已经在内存中的数据结构的指针要糟糕得多。
另一个选择是将监听光标移动到键并发送信号。信号后,C#监听器将知道光标在更新的键处具有值。这种方式解决了数据传输部分的问题,但使用WaitHandles会阻塞——在我的用例中,阻塞比套接字[分配/复制/延迟]组合更糟糕。
还有更好的选择吗?
其他问题:
- 我在重新发明轮子吗?
- 是否可以指向.NET程序等待(非阻塞)C/C++信号的开源库(如果存在)?
- 我应该为此工作流使用LMDB吗? Windows是我的首选,我一直在尝试使LevelDB正常工作(停止尝试)。 LMDB的替代方案是否更好,包括信号传递?
更新
我在文档中找到了此方法:
public static Task WaitOneAsync(this WaitHandle waitHandle)
{
if (waitHandle == null) throw new ArgumentNullException("waitHandle");
var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
delegate { tcs.TrySetResult(true); }, null, -1, true);
var t = tcs.Task;
t.ContinueWith(_ => rwh.Unregister(null));
return t;
}
doc 中的 ThreadPool.RegisterWaitForSingleObject
说明:
等待操作由线程池中的线程执行。当对象的状态变为 signaled 或超时时间到达时,工作线程将执行委托。... 等待线程使用 Win32 的 WaitForMultipleObjects 函数来监视已注册的等待操作。
我的理解是这两个是不同的线程,如果没有信号,第一个等待线程是唯一会阻塞的吗?
RegisterWaitForSingleObject
的效率了解不多。我猜测它是基于IO完成端口的,因此应该是相当高效的。我认为没有专门等待该句柄的线程。可以考虑让其他进程发送一个1字节的命名管道消息(即虚拟消息)。然后,可以使用NamedPipeStream.ReadAsync来进行完全异步等待。 - usr