Pergunta

Como iniciar um evento de um programa C# separado?

O programa C# em que estou trabalhando tem dois componentes separados:Criador de Tarefas (TC) e Processador de Tarefas (TP).O TC está anexado a um serviço web que insere constantemente novas tarefas em uma tabela de banco de dados.Por outro lado, o TP lê a mesma tabela e processa cada tarefa e, em seguida, atualiza o status de volta para a mesma tabela.É quase como uma fila, mas é feito usando banco de dados em vez de, por exemplo,MSMQ.A cada 5 segundos, o TP acorda e lê a tabela para verificar tarefas não processadas, no entanto, isso tem alguns impactos no desempenho.Quando não há novas tarefas, é um desperdício iniciar uma instrução SQL select from where.O ideal é ter algum tipo de método de notificação que quando novas tarefas forem inseridas o TP seja notificado e então acorde do sonho para verificar se há novas tarefas.

Solução atual:

diagrama da solução atual http://yuml.me/3b49bcfd

Solução ideal:

diagrama da solução ideal http://yuml.me/5cf843a5

Foi útil?

Solução

Eu recomendaria MSMQ.:) Não sei por que você não o está usando, mas você mencionou isso em sua pergunta.É exatamente para isso que o MSMQ foi projetado... eventos duráveis ​​entre aplicativos.Ele suporta principalmente o modelo de mensagem pub/sub...que, baseado na sua "Solução Ideal", é exatamente o que você precisa:TC é o editor, TP é um assinante.O TC registra a tarefa e, em seguida, descarta uma mensagem na fila de publicação.A tarefa TP não precisa estar ativa e em execução para que o TC descarte com êxito uma mensagem em sua fila; no entanto, quando a tarefa TP ESTIVER em execução, ela receberá notificações e tratará as mensagens na fila na ordem priorizada de chegada.

Se o MSMQ não for uma opção, você também poderá usar o WCF.Em vez de pub/sub, com o WCF você pode optar por um modelo de mensagem FAF (disparar e esquecer).TP publicaria um serviço que TC consumiria.O TC precisaria apenas enviar uma mensagem ao serviço do TP para notificá-lo sobre novas tarefas.A desvantagem deste modelo é que o TC depende do TP, o que pode não ser uma ideia.O TP também precisa estar em execução para que o TC funcione com êxito, pois depende do serviço do TP.Com a abordagem MSMQ, nem TP nem TC dependem um do outro, eles dependem apenas do MSMQ (uma abordagem de acoplamento inferior).

EDITAR:

Um exemplo de como usar o MSMQ para disparar eventos do TC e responder a eventos no 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;
      }
    }
  }
}

A mensagem não precisa ser um texto simples.Você pode enviar um objeto ou gráfico de objeto, e ele será automaticamente serializado e formatado como XML por padrão.Acredito que você também pode serializar dados em formato binário, se for disso que você precisa.De qualquer forma, você notará que não há chamadas Thread.Sleep ou pesquisas em qualquer lugar.O loop sai com base em um ManualResetEvent, permitindo que você termine o thread de forma limpa, sem uma interrupção forçada.

Outras dicas

Quando não há novas tarefas, é um pouco de desperdício para iniciar uma seleção de onde a instrução SQL.

Uma seleção em uma única tabela, a cada 5 segundos com critérios simples e nenhuma linha retornada geralmente custa quase nada, não se preocupe.

Verificação de saída Notificações de consulta do SQL Server 2005. Acabei de aprender que parece que foi retirado do SQL Server 2008, para que não seja a melhor idéia, afinal.

Segundo que o MSMQ provavelmente é uma maneira muito melhor. Combinado com NSERVICEBUS Você pode ter um vencedor.

Outra abordagem pode ser eventos de sincronização de threads.

Em TP, você poderia ter algo como:

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

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

E em TC:

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

CreateNewTask();
taskEvent.Set();

Não que eu veja como uma ligação a cada cinco segundos pode ser um porco de desempenho.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top