异步事件处理程序处理导致意外的执行流程

问题描述 投票:0回答:1

我正在开发一个遗留应用程序,该应用程序中有一个错误,并最终将其跟踪到一些以 event1 的形式执行的异步事件处理程序,以编程方式触发 event2。原始代码问题如下所示,为了阅读它,我简化了代码:

Public Async Sub MyBaseLoad() Handles MyBase.Load
    //some async operations happen up here
    Method1()
    SomeMethodHere()
End Sub

Public Sub Method1()
    ' Set DataSource which triggers the SelectedIndexChanged event
    myListBox.DataSource = myDataTable
End Sub

Public Async Sub Method2() Handles myListBox.SelectedIndexChanged
    ' Perform Basic Logic
    Await LoadMyData()
    Method3()
End Sub

Public Async Function LoadMyData() As Task(Of Boolean)
    ' Perform basic logic
    Await CustomMethod()
End Function

Public Sub Method3()
    ' Code here not important
End Sub

发生的事情是,当

MyBaseLoad
通过
Method1
执行时,其处理触发了
SelectedIndexChanged
事件,该事件最终得到了
Await LoadMyData()
并且当它到达调用堆栈中的最后一个 Await 时 -
CustomMethod 
,它一直返回执行到
MyBaseLoad
,并且
SomeMethodHere
Method1
完成之前正在执行。因此,在
SomeMethodHere
之前调用
Method3
并产生了 bool 值错误的问题。我认为通过maybe使流程异步并等待,它会按照我想要的方式工作,所以我尝试了这样的事情:

Public Async Sub MyBaseLoad() Handles MyBase.Load
    ' Execute Method1 asynchronously
    Await Method1()
    SomeMethodHere()
End Sub

Public Async Function Method1() As Task
    ' Await MyListBoxPropAssigment asynchronously
    Await MyListBoxPropAssigment()
End Function

Private Async Function MyListBoxPropAssigment() As Task
    ' Set DataSource which triggers the SelectedIndexChanged event
    myListBox.DataSource = myDataTable
    Await Task.Delay(0)
End Function

Public Async Sub Method2() Handles myListBox.SelectedIndexChanged
    ' Perform Basic Logic
    Await LoadMyData()
    Method3()
End Sub

Public Async Function LoadMyData() As Task(Of Boolean)
    ' Perform basic logic
    Await CustomMethod()
End Function

Public Sub Method3()
    ' Code here not important
End Sub

这并没有改变任何东西,并且

SomeMethodHere()
仍在
Method3()
之前执行。现在,我可以通过使用
TaskCompletionSource(Of Boolean)
并更改
MyListBoxPropAssigment()
来等待
TaskCompletionSource
的任务来解决这个问题,其中结果在 Method2() 结束时设置为 true,但我不喜欢解决方案,我也不确定这是否是一个“好的”解决方案。有谁能够解释异步事件处理程序如何处理以及什么是好的解决方案?我认为触发
MyBase.Load
事件并不理想,防止其在 Load 上整个执行的 bool 是一种解决方案,但想了解核心问题。
    

c# .net vb.net asynchronous async-await
1个回答
0
投票

SelectedIndexChanged 的概念,以应对现有基础设施的限制。在您的情况下,您有两个同时运行的 async void

 事件处理程序,因此它们的执行顺序是不可预测的。
我不认为这个问题有任何漂亮的解决方案。您选择的解决方案(在表单中添加
async void
字段)对我来说听起来相当脆弱。它使本来就很复杂的问题变得更加复杂。我更愿意使用经典的解决方法,即使用

TaskCompletionSource<bool>

字段暂时挂起事件处理程序的执行。在我的项目中我通常这样做:

表格顶部:
bool

然后我添加一个实用程序方法,该方法调用同步 (

private bool _eventsDisabled;

) 或异步 (

Action
) 操作,禁用事件处理程序,直到操作完成:
Func<Task>
所有事件处理程序的顶部:

private async Task WithEventsDisabled(Func<Task> action)
{
    bool oldEventsDisabled = _eventsDisabled;
    _eventsDisabled = true;
    try
    {
        await action();
    }
    finally
    {
        _eventsDisabled = oldEventsDisabled;
    }
}

使用

private async void myListBox_SelectedIndexChanged(object sender, EventArgs e) { if (_eventsDisabled) return; //... }
 实用方法来包装引发事件并造成混乱的调用:

WithEventsDisabled

我认为这种方式在最初的开发和维护过程中更容易理解正在发生的事情。

	

© www.soinside.com 2019 - 2024. All rights reserved.