我遇到一种情况,我需要在 2 个方法中的 1 个方法结束时执行一些代码 - 以第二个完成者为准。我的特殊情况是 Blazor 服务器,我需要在 OnInitializedAsync()/ OnAfterRenderAsync() 末尾执行此代码。在这种情况下,代码取决于可调用的 JavaScript 以及从数据库完全填充的页面模型。
我想出了以下课程来完成此任务:
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;
}
}
}
}
然后,我的 razor.cs 文件中有以下内容(这两个方法可以位于不同的任务中,因此可以同时执行):
OnInitializedAsync() {
// ... lots of DB access
if (DoubleFinish.TryFirstFinished)
await OnAfterInitializeAndRenderAsync();
}
OnAfterRenderAsync(bool firstRender) {
if (!firstRender)
return;
if (DoubleFinish.TrySecondFinished)
await OnAfterInitializeAndRenderAsync();
}
我有两个问题:
DoubleFinish
volatile
中声明布尔值吗?lock
?更新: 一个主要限制。在某些配置中,
OnInitializedAsync()
可以被调用两次。所以我不能使用计数器。我必须具体跟踪每个方法是否已完成。
Interlocked.Decrement
方法会更简单,并原子地减少整数计数器。当计数器减至零时,您可以确定最后一个挂起的操作已完成:
private int _pendingCount = 2;
OnInitializedAsync()
{
// ...
if (Interlocked.Decrement(ref _pendingCount) == 0)
await OnAfterInitializeAndRenderAsync();
}
OnAfterRenderAsync(bool firstRender)
{
// ...
if (Interlocked.Decrement(ref _pendingCount) == 0)
await OnAfterInitializeAndRenderAsync();
}
Interlocked.Increment
:
private int counter = 2;
OnInitializedAsync()
{
// ...
if (Interlocked.Increment(ref counter) == 2)
YourCodeToRun();
}
OnAfterRenderAsync(bool firstRender)
{
// ...
if (Interlocked.Increment(ref counter) == 2)
YourCodeToRun();
}
或
SemaphoreSlim
或 AsyncCountdownEvent
来自 Nito.AsyncEx.Coordination
。类似以下内容(如果在 Blazor 上下文中可行):
private AsyncCountdownEvent signal = new AsyncCountdownEvent(2);
Task.Run(async () =>
{
await signal.WaitAsync();
YourCodeToRun();
})
OnInitializedAsync()
{
// ...
signal.Signal();
}
OnAfterRenderAsync(bool firstRender)
{
// ...
signal.Signal();
}