Как выполнить управляемое событиями программирование между двумя отдельными программами C#?
Как запустить событие из отдельной программы на C#?
Программа на C#, над которой я работаю, состоит из двух отдельных компонентов: Task Creator (TC) и Task Processor (TP). TC привязан к веб-сервису, который постоянно вставляет новые задачи в таблицу базы данных. С другой стороны, TP читает из той же таблицы и обрабатывает каждую задачу, а затем обновляет статус до той же таблицы. Это почти как очередь, но сделано с использованием базы данных, а не, например, MSMQ. Каждые 5 секунд TP просыпается и читает данные из таблицы, чтобы проверить наличие необработанных задач, однако это оказывает некоторое влияние на производительность. Когда нет новых задач, это пустая трата, чтобы запустить оператор выбора из SQL. Идеальный способ - это использовать какой-то метод уведомления, чтобы при вставке новых задач TP получал уведомления, а затем просыпался от своей мечты, чтобы проверять наличие новых задач.
Текущее решение:
Идеальное решение:
3 ответа
Я бы порекомендовал MSMQ.:) Я не уверен, почему вы не используете его, однако вы упомянули об этом в своем вопросе. Это в значительной степени именно то, что MSMQ был разработан для... длительных событий между приложениями. Он в первую очередь поддерживает модель сообщений pub/sub... которая на основе вашего "Идеального решения" - именно то, что вам нужно: TC - это издатель, TP - подписчик. TC регистрирует задачу, а затем отбрасывает сообщение в своей очереди публикации. Задача TP не должна быть запущена и запущена для TC, чтобы успешно отбросить сообщение в своей очереди, однако, когда задача TP работает, она будет получать уведомления и обрабатывать сообщения в очереди в порядке приоритета поступления.
Если MSMQ не вариант, вы также можете использовать WCF. Вместо pub/sub, с WCF вы можете выбрать модель сообщения FAF (запустить и забыть). TP будет публиковать сервис, который будет использовать TC. TC нужно будет только отправить сообщение службе TP, чтобы уведомить TP о новых задачах. Недостатком этой модели является то, что TC зависит от TP, что может быть меньше, чем идея. TP также должен быть запущен для успешного функционирования TC, так как он зависит от сервиса TP. При использовании подхода MSMQ ни TP, ни TC не зависят друг от друга, они зависят только от MSMQ (подход с более низкой связью).
РЕДАКТИРОВАТЬ:
Пример использования MSMQ для запуска событий из TC и ответа на события в TP.
// TC message queue manager, sends messages
public class TaskMessageQueueManager
{
public void NotifySubscribersOfNewTasks()
{
var queue = getQueue(".\private$\TaskNotifications");
queue.Send("Tasks waiting.");
}
private MessageQueue getQueue(string name)
{
MessageQueue queue = null;
try
{
if (!MessageQueue.Exists(name))
{
queue = MessageQueue.Create(name);
}
else
{
queue = new MessageQueue(name);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("An error occurred while retrieving the message queue '" + name + "'.", ex);
}
return queue;
}
}
// TP message queue handler, receives messages
public class TaskMessageQueueHandler
{
private Thread m_thread;
private ManualResetEvent m_signal;
public void Start()
{
m_signal = new ManualResetEvent(false);
m_thread = new Thread(MSMQReceiveLoop);
m_thread.Start();
}
public void Stop()
{
m_signal.Set();
}
private void MSMQReceiveLoop()
{
bool running = true;
MessageQueue queue = getQueue(".\private$\TaskNotifications");
while (running)
{
try
{
var message = queue.Receive(); // Blocks here until a message is received by MSMQ
if (message.Body.ToString() == "Tasks waiting.")
{
// TODO: Fire off process, perhaps another thread, to handle waiting tasks
}
if (m_signal.WaitOne(10)) // Non-blocking check for exit signal
{
running = false; // If Stop method has been called, the signal will be set and we can end loop
}
}
catch
{
// handle error
running = false;
}
}
}
}
Сообщение не должно быть простым текстом. Вы можете отправить объект или граф объекта, и он будет автоматически сериализован и отформатирован как XML по умолчанию. Я считаю, что вы также можете сериализовать данные в двоичном формате, если это то, что вам нужно. В любом случае, вы заметите, что нигде нет вызовов или опросов Thread.Sleep. Цикл завершается на основе ManualResetEvent, что позволяет вам аккуратно завершить поток без жесткого прерывания.
Когда нет новых задач, это пустая трата, чтобы запустить оператор выбора из SQL.
Не беспокойтесь, выбор в одной таблице, каждые 5 секунд с простыми критериями и ни одной возвращенной строкой, как правило, почти ничего не стоит.
Ознакомьтесь с уведомлениями о запросах SQL Server 2005. Я только что узнал, что кажется, что он взят из SQL Server 2008, так что это может быть не самая лучшая идея в конце концов.
Во-вторых, MSMQ, вероятно, намного лучше. В сочетании с nServiceBus вы можете получить победителя.
Другим подходом могут быть события синхронизации потоков.
В TP у вас может быть что-то вроде:
EventWaitHandle taskEvent = new EventWaitHandle(true,
EventResetMode.AutoReset,
"newTask",
out wasCreated);
new Thread(WaitForTask).Start();
...
public void WaitForTask() { while (true) { taskEvent.WaitOne(); ProcessTasks();} }
И в ТС:
bool eventExist;
while (!eventExist)
{
try
{
taskEvent= EventWaitHandle.OpenExisting("newTask");
eventExist = true;
}
catch (WaitHandleCannotBeOpenedException)
{
eventExist = false;
Thread.Sleep(1000);
}
}
CreateNewTask();
taskEvent.Set();
Не то чтобы я видел, как вызов каждые пять секунд может быть таким боровом производительности.