C #: eventos Thread-safe
-
10-07-2019 - |
Pergunta
A implementação abaixo thread-safe? Se não o que eu estou ausente? Devo ter a algum lugar palavras-chave volatile
? Ou em algum lugar de bloqueio no método OnProcessingCompleted
? Se sim, onde?
public abstract class ProcessBase : IProcess
{
private readonly object completedEventLock = new object();
private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted
{
add
{
lock (completedEventLock)
ProcessCompleted += value;
}
remove
{
lock (completedEventLock)
ProcessCompleted -= value;
}
}
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted;
if (handler != null)
handler(this, e);
}
}
Nota: A razão pela qual eu tenho evento privado e material de interface explícita, é porque é uma classe base abstrata. E as classes que herdam dele não deve fazer nada com esse evento diretamente. Adicionado o wrapper classe para que ele é = mais claras)
Solução
Não há necessidade do membro ProcessCompleted
privada para ser um event
- que poderia ser apenas um campo:. private EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
- dentro da classe que sempre vai direto para o campo, de modo que o material event
é perdido de qualquer maneira
A abordagem que você mostrou com um objeto de bloqueio explícito não é muito mais thread-safe do que apenas ter um campo-like evento (ie public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
- a única diferença é que você não está bloqueio "isto" (que é uma coisa boa - você deve idealmente bloqueio evitar nas this
) .. a abordagem "variável manipulador" é o caminho certo, mas ainda há
Outras dicas
Você precisa bloquear quando você buscar o manipulador também, caso contrário você não pode ter o valor mais recente:
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler;
lock (completedEventLock)
{
handler = ProcessCompleted;
}
if (handler != null)
handler(this, e);
}
Note que esta não prevenir uma condição de corrida onde nós decidimos que vamos executar um conjunto de manipuladores e então um manipulador não subscritas. Ele ainda vai ser chamado, porque temos buscado o delegado multicast contendo-o na variável handler
.
Não há muita coisa que você pode fazer sobre isso, além de fazer o manipulador própria consciência de que não deve ser chamado mais.
É, sem dúvida, melhor apenas não tente para fazer os eventos thread-safe - especificar que a assinatura deve única mudança na discussão que irá aumentar o evento