如何在两个独立的 C# 程序之间进行事件驱动编程?

发布于 2024-08-03 06:18:00 字数 580 浏览 11 评论 0原文

如何从单独的 C# 程序启动事件?

我正在开发的 C# 程序有两个独立的组件:任务创建器 (TC) 和任务处理器 (TP)。 TC 附加到一个 Web 服务,该服务不断地将新任务插入数据库表中。另一方面,TP 从同一个表中读取并处理每个任务,然后将状态更新回同一个表。它几乎就像一个队列,但使用数据库而不是 MSMQ 来完成。 TP 每 5 秒醒来并从表中读取以检查未处理的任务,但这会对性能产生一些影响。当没有新任务时,启动 select from where SQL 语句有点浪费。理想的方法是有某种通知方法,当插入新任务时,TP 会收到通知,然后从梦中醒来检查是否有新任务。

当前解决方案:

当前解决方案图 http://yuml.me/3b49bcfd

< strong>理想解决方案:

理想解决方案图http://yuml.me/5cf843a5

How to fire up event from a separate C# program?

The C# program I am working on has two separate components: Task Creator (TC) and Task Processor (TP). TC is attached to a web service that constantly inserts new tasks into a database table. On the other hand TP reads from the same table and processes each tasks then updates the status back to the same table. It’s almost like a queue but done using database rather than e.g. MSMQ. Every 5 seconds TP wakes up and reads from the table to check for unprocessed tasks however this has some performance impacts. When there’s no new tasks it’s a bit of waste to fire up a select from where SQL statement. The ideal way is to have some kind of notification method that when new tasks are inserted TP will get notified and then wakes up from its dream to check for new tasks.

Current solution:

diagram of current solution http://yuml.me/3b49bcfd

Ideal solution:

diagram of idealsolution http://yuml.me/5cf843a5

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

等往事风中吹 2024-08-10 06:18:00

我会推荐MSMQ。 :) 我不确定您为什么不使用它,但是您在问题中确实提到了它。这几乎正​​是 MSMQ 的设计目的……应用程序之间的持久事件。它主要支持发布/订阅消息模型...基于您的“理想解决方案”正是您所需要的:TC 是发布者,TP 是订阅者。 TC 注册任务,然后将消息放入其发布队列中。 TP 任务不需要启动并运行,TC 才能成功删除其队列中的消息,但是当 TP 任务运行时,它将接收通知并按照到达的优先顺序处理队列中的消息。

如果 MSMQ 不是一个选项,您也可以使用 WCF。使用 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 退出,允许您干净地结束线程而无需硬中止。

I would recommend MSMQ. :) I am not sure why you are not using it, however you did mention it in your question. This is pretty much exactly what MSMQ was designed for...durable eventing between applications. It primarily supports the pub/sub message model...which based on your "Ideal Solution" is exactly what you need: TC is the publisher, TP is a subscriber. TC registers the task, then drops a message in its publish queue. The TP task does not need to be up and running for TC to successfully drop a message in its queue, however when the TP task IS running, it will receive notifications and handle messages in the queue in the prioritized order the arrive.

If MSMQ is not an option, you could also use WCF. Rather than pub/sub, with WCF you could opt for a FAF (fire and forget) message model. TP would publish a service which TC would consume. TC would only need to fire a message off to TP's service to notify TP of new tasks. The downside of this model is that TC is dependent on TP, which might be less than idea. TP also has to be running for TC to successfully function, since it is dependent upon TP's service. With the MSMQ approach, neither TP nor TC are dependent on each other, they are only dependent upon MSMQ (a lower-coupling approach.)

EDIT:

An example of how to use MSMQ to fire events from TC and respond to events in 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;
      }
    }
  }
}

The message does not have to be simple text. You can send an object or object graph, and it will automatically be serialized and formatted as XML by default. I believe you can also serialize data in a binary format, if that is what you need. Either way, you'll notice that there are no Thread.Sleep calls or polling anywhere. The loop exits based on a ManualResetEvent, allowing you to cleanly end the thread without a hard abort.

世态炎凉 2024-08-10 06:18:00

当没有新任务时,启动 select from where SQL 语句有点浪费。

在单个表上进行选择,每 5 秒一次,具有简单的条件并且不返回任何行,通常几乎不需要任何成本,不用担心。

When there’s no new tasks it’s a bit of waste to fire up a select from where SQL statement.

A select on a single table, every 5 secs with simple criteria and none rows returned usually costs next to nothing, dont worry.

冷默言语 2024-08-10 06:18:00

查看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();} }

在 TC 中:

bool eventExist;
while (!eventExist)
{
    try
    {
        taskEvent= EventWaitHandle.OpenExisting("newTask");
        eventExist = true;
    }
    catch (WaitHandleCannotBeOpenedException)
    {
        eventExist = false;
        Thread.Sleep(1000);
    }
}

CreateNewTask();
taskEvent.Set();

并不是说我认为每五秒一次的调用会如此消耗性能。

Check out SQL Server 2005 Query Notifications. I just learned that it seems that it's been pulled from SQL Server 2008 so it might not be the best idea after all.

I second that MSMQ is probably a much better way. Combined with nServiceBus you could have a winner.

Another approach could be thread synchronization events.

In TP you could have something like:

EventWaitHandle taskEvent = new EventWaitHandle(true,
                EventResetMode.AutoReset,
                "newTask",
                out wasCreated);
new Thread(WaitForTask).Start();
...

public void WaitForTask() { while (true) { taskEvent.WaitOne(); ProcessTasks();} }

And in TC:

bool eventExist;
while (!eventExist)
{
    try
    {
        taskEvent= EventWaitHandle.OpenExisting("newTask");
        eventExist = true;
    }
    catch (WaitHandleCannotBeOpenedException)
    {
        eventExist = false;
        Thread.Sleep(1000);
    }
}

CreateNewTask();
taskEvent.Set();

Not that I see how a call every five second could be such a performance hog.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文