Я программирую в основном на .NET, и мне нравятся его асинхронные / параллельные примитивы, такие как Tasks, ResetEvents и т. Д. Сегодня я впервые внес значительные изменения в программу на C ++ и понял, как работает весь процесс сборки (я обновленный Проект LigthningDB.NET до 0.9.14). Но мне все еще не хватает знаний C ++.
Одна новая функция, которую я хочу добавить в проект LMDB (для собственных нужд), — это система уведомлений (похожая на Redis):
Типичный вариант использования — это писатель и N читателей, ожидающих новых сообщений. Это позволит IPC и быстрое сохранение 2-в-1. LMDB поддерживает одновременное чтение из разных процессов (в то время как Esent и LevelDB — нет).
System.Threading
Примитивы доступны из C ++, и я понимаю, как использовать WaitHandle для блокирующего вызова. Есть ли способ сделать этот асинхронный? Eсть большая серия статей об асинхронных примитивах синхронизации, но они используют TaskCompletionSource
и это работает только внутри .NET. Можно ли сделать подобное решение для родного взаимодействия?
Одним из решений могут быть именованные каналы или сокеты, транслирующие изменения одному слушателю / диспетчеру (Redis или Rhino.Queues стиль), но производительность пострадает: записи должны будут распределять, копировать и передавать данные, а данные должны перемещаться — гораздо хуже, чем передавать указатель на структуру данных, которая уже находится в памяти.
Другой вариант — переместить курсор прослушивания на клавишу и отправить сигнал. После сигнала слушатель C # будет знать, что курсор имеет значения при обновленной клавише. Этот вид решает часть передачи данных, но с WaitHandles это блокировка — в моем случае блокировка хуже, чем комбинация сокетов [размещение / копирование / задержка].
Есть ли лучшие варианты?
Дополнительный вопрос:
Обновить
я нашел в документации Этот метод:
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 функция для мониторинга зарегистрированных операций ожидания.
Правильно ли я понимаю, что это два разных потока, и первый поток ожидания является единственным, который будет блокировать, если нет сигналов?
RWFSO использует специальные потоки пула потоков для ожидания нескольких дескрипторов. Существует встроенное ограничение в 63 дескриптора на поток, так что это не так эффективно, как IOCP. Я бы не рекомендовал ручное решение, хотя MRE Можно быть использованы для решения этого (MRE могут быть использованы для решения практически все …).
В C ++ нет понятия «события». Традиционный подход заключается в получении указателя на функцию обратного вызова (которая в сочетании с Boost.Bind
а также Boost.Function
не так больно в наши дни). Более современный подход будет Boost.Signals2
.
Обновление: был логический недостаток: чтобы использовать обратный вызов C, должен быть один процесс C и несколько процессов C #. Но с MMF, только память разделяется B / W 2 различных процессов C. Поэтому мне нужен либо сервер C, такой как Redis (но у нас уже есть Redis!), Либо межпроцессная сигнализация.
У меня был опыт работы с надежная синхронизация и редис и это было корнем вопроса — я хотел иметь подобное решение, но для локальной машины с постоянством.
Вероятно, переписать логику Redis с помощью именованных MRE / ARE — это самый простой подход. Как сказал Стивен, MRE может сделать что угодно.
(любые голоса должны идти к ответу Стивена Клири, вот только пример)
Ответ заключается в использовании 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);
}
C:
// 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