提问人:David Thielen 提问时间:11/11/2023 最后编辑:David Thielen 更新时间:11/13/2023 访问量:94
如何在第二个方法完成时执行代码
How to execute code when the second method completes
问:
我有一种情况,我需要在 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()
{
// ...
if (Interlocked.Decrement(ref _pendingCount) == 0)
await OnAfterInitializeAndRenderAsync();
}
更新:如果这两种方法中的每一个都可以多次调用,则它会变得更加复杂,但您仍然可以使用 Interlocked
类。以下是该类型的原子按位 OR 运算的实现:uint
/// <summary>
/// Returns the result of an atomic bitwise OR operation.
/// </summary>
static uint AtomicBitwiseOr(ref uint location, uint operand)
{
uint oldValue = location;
while (true)
{
uint newValue = oldValue | operand;
uint original = Interlocked.CompareExchange(ref location, newValue, oldValue);
if (original == oldValue) return newValue;
oldValue = original;
}
}
它可以这样使用:
private uint _state = 0;
OnInitializedAsync()
{
// ...
if (AtomicBitwiseOr(ref _state, 0b1) == 0b11)
await OnAfterInitializeAndRenderAsync();
}
OnAfterRenderAsync()
{
// ...
if (AtomicBitwiseOr(ref _state, 0b10) == 0b11)
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();
}
或 AsyncCountdownEvent
。类似于以下内容(如果在 Blazor 上下文中可行):SemaphoreSlim
Nito.AsyncEx.Coordination
private AsyncCountdownEvent signal = new AsyncCountdownEvent(2);
Task.Run(async () =>
{
await signal.WaitAsync();
YourCodeToRun();
})
OnInitializedAsync()
{
// ...
signal.Signal();
}
OnAfterRenderAsync(bool firstRender)
{
// ...
signal.Signal();
}
VPg公司
如果可以多次运行,那么您的解决方案应该没问题。请注意,您应该避免使用 ,只需创建一个用于锁定的字段,并且不需要 volatile,因为您仅在锁内使用布尔值。OnInitializedAsync
lock(this)
private object _locker = new object();
或者你可以玩这样的怪物(尽管我会坚持锁定):
private int flag1 = 0;
private int flag2 = 0;
private int hasRun = 0;
OnInitializedAsync()
{
// ...
Interlocked.Exchange(ref flag1, 1);
if(Interlocked.Read(ref flag1) + Interlocked.Read(ref flag2) == 2 && Interlocked.Exchange(ref hasRun, 1) == 0)
YourCodeToRun();
}
OnAfterRenderAsync(bool firstRender)
{
// ...
Interlocked.Exchange(ref flag2, 1);
if(Interlocked.Read(ref flag1) + Interlocked.Read(ref flag2) == 2 && Interlocked.Exchange(ref hasRun, 1) == 0)
YourCodeToRun();
}
评论
OnInitializedAsync()
Interlocked.Exchange()
Interlocked.Exchange()
Interlocked
lock
首先,我建议阅读其他答案和评论。这有很多微妙之处,讨论贯穿了所有这些。非常值得花时间。
其次,对于我的特定用例,锁不是必需的。虽然 和 ,但它们都在同一个 UI 线程中执行。因此,它们不能同时调用这些方法。OnInitializeAsync()
OnAfterRenderAsync()
Try
第三,如果你确实需要这个功能,其中调用可以来自不同的线程,请使用我的方法。使用的其他方法进行安全的单独调用。但是连续 2 或 3 次调用不是单个原子操作。它们是 2 或 3 个离散的原子动作。对于此用例,您需要将所有这些操作作为单个原子操作。lock
Interlocked
Interlocked
评论
volatile
lock (this)
OnInitializedAsync()