4

如何从单独的 C# 程序中触发事件?

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

当前解决方案:

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

理想的解决方案:

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

4

3 回答 3

4

我会推荐 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 退出,允许您干净地结束线程而无需硬中止。

于 2009-08-27T06:48:37.460 回答
2

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

并不是说我看到每五秒打一次电话会如此消耗性能。

于 2009-08-27T06:49:09.750 回答
2

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

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

于 2009-08-27T06:51:13.303 回答