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

时间:2009-08-27 06:42:14

标签: c# event-handling eai

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

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

当前解决方案:

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

理想解决方案:

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

3 个答案:

答案 0 :(得分:4)

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

如果MSMQ不是一个选项,您也可以使用WCF。使用WCF而不是pub / sub,您可以选择FAF(fire and forget)消息模型。 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退出,允许您在没有硬中止的情况下干净地结束线程。

答案 1 :(得分:2)

查看SQL Server 2005 Query Notifications 。我刚刚了解到它似乎是从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();

不是说我看到每五秒一次电话怎么可能是这样的表现。

答案 2 :(得分:2)

  

当没有新的任务时,从SQL语句的位置启动select会有点浪费。

单个表上的选择,每5秒使用简单的条件并且没有返回的行通常几乎没有任何成本,不用担心。