Tenho uma situação em que preciso executar algum código no final de 1 de 2 métodos - o que for concluído em segundo lugar. Meu caso particular é o servidor Blazor onde preciso executar este código no final de OnInitializedAsync()/OnAfterRenderAsync(). Nesse caso, o código depende de o JavaScript poder ser chamado e de o modelo da página ser totalmente preenchido a partir do banco de dados.
Eu criei a seguinte classe para fazer isso:
public class DoubleFinish
{
private volatile bool _firstFinished = false;
private volatile bool _secondFinished = false;
private volatile bool _alreadyReturnedTrue = false;
/// <summary>
/// Call when the first method completes. Returns true if the 2nd method is also
/// complete and so this method can now run the code that requires both methods.
/// </summary>
public bool TryFirstFinished
{
get
{
lock (this)
{
_firstFinished = true;
if (_alreadyReturnedTrue || ! _secondFinished)
return false;
_alreadyReturnedTrue = true;
return true;
}
}
}
/// <summary>
/// Call when the second method completes. Returns true if the 1st method is also
/// complete and so this method can now run the code that requires both methods.
/// </summary>
public bool TrySecondFinished
{
get
{
lock (this)
{
_secondFinished = true;
if (_alreadyReturnedTrue || ! _firstFinished)
return false;
_alreadyReturnedTrue = true;
return true;
}
}
}
}
Então, em meu arquivo razor.cs, tenho o seguinte (os dois métodos em que estão podem estar em tarefas diferentes e, portanto, estar em execução ao mesmo tempo):
OnInitializedAsync() {
// ... lots of DB access
if (DoubleFinish.TryFirstFinished)
await OnAfterInitializeAndRenderAsync();
}
OnAfterRenderAsync(bool firstRender) {
if (!firstRender)
return;
if (DoubleFinish.TrySecondFinished)
await OnAfterInitializeAndRenderAsync();
}
Eu tenho duas perguntas:
- Preciso declarar os bools em
DoubleFinish
volatile
? - Existe algum tipo de chamada atômica para verificar/definir os valores bool que evita o uso de
lock
?
Atualização: uma restrição importante. OnInitializedAsync()
pode ser chamado duas vezes em algumas configurações. Então não posso usar um contador. Tenho que monitorar especificamente se cada método foi concluído.
Acho que seria mais simples usar o
Interlocked.Decrement
método, e diminuir atomicamente um contador inteiro. Quando o contador chegar a zero, você pode ter certeza de que a última operação pendente foi concluída:Atualizado: Caso cada um dos dois métodos possa ser chamado mais de uma vez, fica mais complexo, mas você ainda pode usar a
Interlocked
classe. Aqui está uma implementação da operação OR bit a bit atômica para ouint
tipo:Poderia ser usado assim:
Uma coisa que você pode fazer é usar
Interlocked.Increment
:Ou
SemaphoreSlim
ouAsyncCountdownEvent
deNito.AsyncEx.Coordination
. Algo como o seguinte (se for viável no contexto do Blazor):Atualização
Se
OnInitializedAsync
puder ser executado várias vezes, sua solução deverá funcionar bem. Observe que você deve evitar usarlock(this)
, basta criar umprivate object _locker = new object();
campo para bloqueio e volátil não deve ser necessário, pois você está usando booleanos apenas dentro de bloqueios.Ou você pode brincar com essa monstruosidade (embora eu preferisse o bloqueio):