4

別のC#プログラムからイベントを起動する方法は?

私が取り組んでいるC#プログラムには、タスククリエーター(TC)とタスクプロセッサー(TP)の2つの別個のコンポーネントがあります。TCは、データベーステーブルに常に新しいタスクを挿入するWebサービスに接続されています。一方、TPは同じテーブルから読み取り、各タスクを処理してから、ステータスを同じテーブルに更新します。これはほとんどキューに似ていますが、MSMQなどではなくデータベースを使用して実行されます。5秒ごとに、TPはウェイクアップし、テーブルから読み取り、未処理のタスクをチェックしますが、これにはパフォーマンスへの影響があります。新しいタスクがない場合、SQLステートメントからselectを起動するのは少し無駄です。理想的な方法は、新しいタスクが挿入されたときにTPに通知が届き、夢から目覚めて新しいタスクをチェックするという、ある種の通知方法を用意することです。

現在の解決策:

現在のソリューションの図http://yuml.me/3b49bcfd

理想的な解決策:

理想的なソリューションの図http://yuml.me/5cf843a5

4

3 に答える 3

4

MSMQをお勧めします。:)なぜあなたがそれを使わないのか分かりませんが、あなたはあなたの質問でそれについて言及しました。これは、MSMQがアプリケーション間の耐久性のあるイベントのために設計されたものとほぼ同じです。これは主にpub/subメッセージモデルをサポートします...これは「理想的なソリューション」に基づいてまさに必要なものです。TCはパブリッシャーであり、TPはサブスクライバーです。TCはタスクを登録し、公開キューにメッセージをドロップします。TCがキューにメッセージを正常にドロップするために、TPタスクが稼働している必要はありませんが、TPタスクが実行されている場合、TPタスクは通知を受信し、キュー内のメッセージを優先順位に従って処理します。

MSMQがオプションでない場合は、WCFを使用することもできます。pub / subではなく、WCFを使用すると、FAF(ファイアアンドフォーゲット)メッセージモデルを選択できます。TPは、TCが消費するサービスを公開します。TCは、TPに新しいタスクを通知するために、TPのサービスにメッセージを送信するだけで済みます。このモデルの欠点は、TCがTPに依存していることです。これは、アイデアよりも少ない可能性があります。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

SQLServer2005クエリ通知を確認してください。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();

5秒ごとの呼び出しが、パフォーマンスを大幅に低下させる可能性があることはわかりません。

于 2009-08-27T06:49:09.750 に答える
2

新しいタスクがない場合、SQLステートメントからselectを起動するのは少し無駄です。

単純な基準で5秒ごとに単一のテーブルを選択し、行が返されない場合、通常はほとんど費用がかかりません。心配する必要はありません。

于 2009-08-27T06:51:13.303 に答える