Pergunta

Eu tenho um método que deve ser executado de uma forma exclusiva. Basicamente, é uma aplicação com rosca de multi onde o método é chamado periodicamente por um temporizador, mas que também pode ser activado manualmente por uma acção do utilizador.

Vamos dar um exemplo:

  1. Os decorre temporizador, de modo que o método é chamado. A tarefa pode demorar alguns segundos.

  2. Logo após, o usuário clica em algum botão, que deve acionar o mesma tarefa: BAM. Ele não faz nada uma vez que o método já está em execução.

Eu usei a seguinte solução:

public void DoRecurentJob()
{
    if(!Monitor.TryEnter(this.lockObject))
    {
        return;
    }

    try
    {
        // Do work
    }
    finally 
    {
        Monitor.Exit(this.lockObject);
    }
}

Onde lockObject é declarado assim:

private readonly object lockObject = new object();

Editar :. Haverá apenas uma instância do objeto que ocupa este método, então eu atualizei o objeto de bloqueio para ser non-static

Existe uma maneira melhor de fazer isso? Ou talvez este é apenas errado, por qualquer motivo?

Foi útil?

Solução

Isso parece razoável, se você está apenas interessado em não ter o método de execução em paralelo. Não há nada para impedi-lo de correr imediatamente após o outro, dizer que você apertou o botão de metade de um microssegundo após o temporizador executou o Monitor.Exit ().

E ter o objeto de bloqueio como estático readonly também faz sentido.

Outras dicas

Você também pode usar Mutex ou Semaphore se você quer que ele funcione processo transversal (com uma ligeira perda de performance), ou se você precisa definir qualquer outro número de um dos threads simultâneos permitidos executando o seu pedaço de código.

Existem outras construções de sinalização que iria funcionar, mas o seu exemplo parece que ele faz o truque, e de uma forma simples e direta.

Menor nit: se a variável LockObject é estático, em seguida, "this.lockObject" não deve compilar. Além disso, considera ligeiramente ímpar (e deve ser, pelo menos, fortemente documentada) que embora este seja um método de exemplo, que tem um tipo de largura distintamente comportamento bem. Possivelmente torná-lo um método estático que leva uma instância como parâmetro?

Será que ela realmente usar os dados de instância? Se não, torná-lo estático. Se isso acontecer, você deve pelo menos retornar um booleano para dizer se está ou não fez o trabalho com a instância - Acho que é difícil imaginar uma situação onde eu quero um pouco de trabalho feito com uma determinada peça de dados, mas eu não faço importo se que o trabalho não é realizado porque algum trabalho semelhante estava sendo realizado com uma peça diferente de dados.

Eu acho que deveria funcionar, mas ele se sente um pouco estranho. Eu não sou geralmente um fã do uso de bloqueio manual só porque é tão fácil de errar - mas isso parece bem. (Você precisa considerar exceções assíncronas entre o "se" e "tentativa", mas eu suspeito que não será um problema - eu não consigo lembrar as garantias exatas feitas pelo CLR.)

Eu acho que a Microsoft recomenda usando o declaração de bloqueio , em vez de usar o monitor classe diretamente. Ele dá um layout mais limpo e garante o bloqueio será liberado em todas as circunstâncias.

public class MyClass
{

  // Used as a lock context
  private readonly object myLock = new object();

  public void DoSomeWork()
  {
    lock (myLock)
    {
      // Critical code section
    }
  }
}

Se a sua aplicação requer o bloqueio para abranger todas as instâncias de MyClass você pode definir o contexto de bloqueio como um campo estático:

private static readonly object myLock = new object();

O código é bom, mas concorda com a mudança do método a ser estático como ele transmite intenção melhor. Parece estranho que todas as instâncias de uma classe tem um método entre eles que é executado de forma síncrona, mas esse método não é estático.

Lembre-se que você sempre pode ter o método syncronous estática a ser protegido ou privado, deixando visível apenas para as instâncias da classe.

public class MyClass
{ 
    public void AccessResource()
    {
        OneAtATime(this);
    }

    private static void OneAtATime(MyClass instance) 
    { 
       if( !Monitor.TryEnter(lockObject) )
       // ...

Esta é uma boa solução, embora eu não estou realmente feliz com o bloqueio estático. Agora você não está aguardando o bloqueio para que você não vai ter problemas com impasses. Mas fazer bloqueios também visível pode facilmente levá-lo para problemas na próxima vez que você tem que editar este código. Também esta não é uma solução muito escalável.

Normalmente, eu tento fazer todos os recursos que tentam proteger contra o acesso de vários segmentos variáveis ??de instância privadas de uma classe e, em seguida, ter um bloqueio como uma variável de instância privada também. Dessa forma, você pode instanciar vários objetos se você precisa de escala.

A forma mais declarativa de fazer isso é usando o MethodImplOptions.Synchronized especificador no método para o qual você deseja acessar sincronizar:

[MethodImpl(MethodImplOptions.Synchronized)] 
public void OneAtATime() { }

No entanto, este método é desencorajado por várias razões, a maioria dos quais podem ser encontrados aqui e aqui . Estou postando isso para que você não vai se sentir tentado a usá-lo. Em Java, synchronized é uma palavra-chave, por isso pode chegar ao rever padrões de encadeamento.

Nós temos uma exigência semelhante, com a exigência acrescentou que se o processo de longa duração é solicitada novamente, deve enfileirar para executar um novo ciclo após o ciclo atual é completa. É semelhante a este:

https://codereview.stackexchange.com / perguntas / 16150 / Singleton-task-running-usando-tarefas esperam por-peer-review-desafio

private queued = false;
private running = false;
private object thislock = new object();

void Enqueue() {
    queued = true;
    while (Dequeue()) {
        try {
            // do work
        } finally {
            running = false;
        }
    }
}

bool Dequeue() {
    lock (thislock) {
        if (running || !queued) {
            return false;
        }
        else
        {
            queued = false;
            running = true;
            return true;
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top