4

私は主に .NET でプログラミングしており、Tasks、ResetEvents などの非同期/同時実行プリミティブが大好きです。今日、初めて C++ プログラムに意味のある変更を加え、ビルド プロセス全体がどのように機能するかを理解しました ( LigthningDB.NET プロジェクトを更新しました)。 0.9.14 に)。しかし、私はまだ C++ の知識が不足しています。

LMDB プロジェクトに (自分のニーズのために) 追加したい新機能の 1 つは、通知システム (Redis に似ています) です。

  • テーブル内のすべてのデータ変更を通知する待機可能なオブジェクトを返すカーソルが必要です。
  • キーまたはキー + 値など、シグナル (データ構造へのポインター) と一緒にデータを取得したい。
  • このオブジェクトはカーソルと共に破棄され、カーソルの一部ですが、カーソルが参照を持つ DB の変更を通知します。
  • これは、いつの日か (または何年か) クロスプラットフォームで動作するはずです。彼らが仕事をするなら、私はどんな汚いWindows固有のハックでもOKです.

典型的なユースケースは、新しいメッセージを待っているライターと N 人のリーダーです。これにより、IPC と高速持続性 2-in-1 が可能になります。LMDB は、異なるプロセスからの同時読み取りをサポートしています (Esent と LevelDB はサポートしていません)。

System.Threadingプリミティブは C++ から利用でき、ブロッキング呼び出しに WaitHandle を使用する方法を理解しています。これを非同期にする方法はありますか? 非同期同期プリミティブに関する優れた一連の記事がありますが、それらはTaskCompletionSource.NET 内でのみ機能する and を使用します。ネイティブ相互運用のために同様のソリューションを作成することは可能ですか?

解決策の 1 つは、変更を 1 つのリスナー/ディスパッチャー (Redis またはRhino.Queuesスタイル) に変換する名前付きパイプまたはソケットである可能性がありますが、パフォーマンスが低下します。すでにメモリ内にあるデータ構造へのポインタ。

もう 1 つのオプションは、リスニング カーソルをキーに移動し、信号を送信することです。信号の後、C# リスナーは、カーソルが更新されたキーに値を持っていることを認識します。この種類はデータ転送部分を解決しますが、WaitHandles を使用するとブロックされます。私の使用例では、ブロックはソケット [割り当て/コピー/遅延] の組み合わせよりも悪いです。

より良いオプションはありますか?

追加の質問:

  1. ここで自転車を再発明していますか?
  2. .NET プログラムが C/C++ シグナル (存在する場合) を待機する (ノンブロッキング) オープン ソース ライブラリを教えてください。
  3. そのワークフローに 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;
}

ドキュメントは次のようにThreadPool.RegisterWaitForSingleObject述べています。

待機操作は、スレッド プールのスレッドによって実行されます。デリゲートは、オブジェクトの状態がシグナル状態になるか、タイムアウト間隔が経過すると、ワーカースレッドによって実行されます。...待機スレッドは、Win32 WaitForMultipleObjects関数を使用して、登録された待機操作を監視します。

これらは 2 つの異なるスレッドであり、シグナルがない場合にブロックされるのは最初の待機スレッドだけであるというのは正しいですか?

4

2 に答える 2

3

RWFSO は、特別なスレッド プール スレッドを使用して、複数のハンドルを待機します。スレッドあたり 63 ハンドルという組み込みの制限があるため、これは IOCP ほど効率的ではありません。MREを使用してこれを解決できますが、ハンドルベースのソリューションはお勧めしません(MREはほとんどすべてを解決するために使用できます...)。

C++ には「イベント」という概念がありません。従来のアプローチは、コールバック関数ポインターを使用することです (これと組み合わせるとBoost.BindBoost.Function最近ではそれほど苦痛ではありません)。より現代的なアプローチはBoost.Signals2.

于 2014-12-25T23:59:39.687 に答える
0

更新: 論理的な欠陥がありました。C コールバックを使用するには、単一の C プロセスと複数の C# プロセスが必要です。しかし、MMF ではメモリのみが B/W 2 つの異なる C プロセスで共有されます。したがって、Redis のような C サーバー (ただし、Redis は既にあります!) またはプロセス間シグナリングが必要です。

私は信頼できる同期とRedisの経験があり、それが質問の根源でした.同様のソリューションが必要でしたが、永続的なローカルマシンが必要でした.

おそらく、Redis ロジックを名前付き MRE/ARE で書き直すのが最も簡単な方法です。スティーブンが言ったように、MRE は何でもできます。


(任意の投票は、Stephen Cleary による回答に送られる必要があります。これは単なる例です)

答えは、TaskCompletionSource と Callback を使用してその結果を設定することです。

コールバックの例:

C#:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int CompareCallback(int a, int b);

[SuppressUnmanagedCodeSecurity]
class NativeMethod
{

    [DllImport("CppDynamicLinkLibrary.dll", CharSet = CharSet.Auto)]
    public static extern int CompareInts(int a, int b, CompareCallback cmpFunc);

}

子:

// Type-definition: 'PFN_COMPARE' now can be used as type
typedef int (CALLBACK *PFN_COMPARE)(int, int);

// An exported/imported stdcall function using a DEF file
// It requires a callback function as one of the arguments
int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc)
{
    // Make the callback to the comparison function

    // If a is greater than b, return a; 
    // If b is greater than or equal to a, return b.
    return ((*cmpFunc)(a, b) > 0) ? a : b;
}

サンプル コード: https://code.msdn.microsoft.com/windowsdesktop/CSPInvokeDll-b05779d0

于 2014-12-27T11:33:35.480 に答える