如何防止一种异步方法垄断另一种方法? [英] How can I prevent one async method from monopolizing another?
问题描述
在我的UWP应用中,我有一个异步方法(事件处理程序),该方法调用另一个异步方法,该方法试图将记录插入数据库中.
In my UWP app, I've got an async method (event handler) that calls another async method, which attempts to insert a record into a database.
我在插入尝试时遇到异常,并试图探究为什么发生这种情况.因此,我在InsertMapRecord()方法中的第一个使用"对象上设置了一个断点.行:
I'm getting an exception in the insertion attempt, and am trying to sherlock why it's happening. So I put a breakpoint in the InsertMapRecord() method, on the first "using" line:
using (SqliteConnection conn = new SqliteConnection(connStr))
当我到达该断点时,我按F10键,但不是将我带到Insert方法的下一行,而是带我到事件处理程序btnCre8NewMap_Click()中的这一行(您已经被点击了,认为,要到达上一行):
When I reach that breakpoint, I hit F10, but instead of taking me to the next line in the Insert method, it takes me to this line in btnCre8NewMap_Click(), the event handler (which has already been hit, you would think, for the previous line to have been reached):
InsertMapRecord(mapName, mapNotes, defaultZoomLevel);
然后我按下F11,以尝试返回InsertMapRecord()方法,但最终我在这行上到达了App.g.i.cs:
I then hit F11, in an attempt to return to the InsertMapRecord() method, but instead I end up at App.g.i.cs, on this line:
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
...带有" global :: System.Diagnostics.Debugger.Break()".突出显示,然后显示以下异常消息:
...with "global::System.Diagnostics.Debugger.Break()" highlighted, and then with this exception message:
完整方法如下
private async void btnCre8NewMap_Click(object sender, RoutedEventArgs e)
{
try
{
string mapName = string.Empty;
string mapNotes = string.Empty;
int defaultZoomLevel = 1;
ClearLocations();
// Popul8 the cmbx
for (int i = 1; i < 20; i++)
{
cmbxCre8MapZoomLevels.Items.Add(i.ToString());
}
ContentDialogResult result = await cntDlgCre8Map.ShowAsync();
if (result == ContentDialogResult.Primary)
{
mapName = txtbxMapName.Text;
mapNotes = txtbxMapNotes.Text;
defaultZoomLevel = cmbxCre8MapZoomLevels.SelectedIndex + 1;
InsertMapRecord(mapName, mapNotes, defaultZoomLevel);
}
// else do nothing (don't save)
}
catch (Exception ex)
{
MessageDialog exceptionMsgDlg = new MessageDialog(ex.Message, "btnCre8NewMap_Click");
await exceptionMsgDlg.ShowAsync();
}
}
private async void InsertMapRecord(string mapName, string mapNotes, int preferredZoomLevel)
{
path = folder.Path;
connStr = string.Format(connStrBase, path);
try
{
using (SqliteConnection conn = new SqliteConnection(connStr))
{
String query = "INSERT INTO dbo.CartographerMain " +
"(MapName, MapNotes, PreferredZoomLevel) " +
"VALUES (@MapName, @MapNotes, @PreferredZoomLevel)";
using (SqliteCommand cmd = new SqliteCommand(query, conn))
{
cmd.Parameters.AddWithValue("@MapName", mapName);
cmd.Parameters.AddWithValue("@MapNotes", mapNotes);
cmd.Parameters.AddWithValue("@PreferredZoomLevel", preferredZoomLevel);
conn.Open();
int result = cmd.ExecuteNonQuery();
if (result < 0)
{
MessageDialog dialog = new MessageDialog("Error inserting data into CartographerMain");
await dialog.ShowAsync();
}
}
}
}
catch (SqliteException sqlex)
{
MessageDialog dialog = new MessageDialog(sqlex.Message, "InsertMapRecord");
await dialog.ShowAsync();
}
}
推荐答案
InsertMapRecord
方法应返回调用者可以等待的 Task
.另外,当您打开与数据库的连接或执行查询时,它也不会阻塞:
The InsertMapRecord
method should return a Task
that can be awaited by the caller. Also, it shouldn't block when you open a connection to the database or execute the query:
private async Task InsertMapRecord(string mapName, string mapNotes, int preferredZoomLevel)
{
path = folder.Path;
connStr = string.Format(connStrBase, path);
try
{
using (SqliteConnection conn = new SqliteConnection(connStr))
{
String query = "INSERT INTO dbo.CartographerMain " +
"(MapName, MapNotes, PreferredZoomLevel) " +
"VALUES (@MapName, @MapNotes, @PreferredZoomLevel)";
using (SqliteCommand cmd = new SqliteCommand(query, conn))
{
cmd.Parameters.AddWithValue("@MapName", mapName);
cmd.Parameters.AddWithValue("@MapNotes", mapNotes);
cmd.Parameters.AddWithValue("@PreferredZoomLevel", preferredZoomLevel);
await conn.OpenAsync();
int result = await cmd.ExecuteNonQueryAsync();
if (result < 0)
{
MessageDialog dialog = new MessageDialog("Error inserting data into CartographerMain");
await dialog.ShowAsync();
}
}
}
}
catch (SqliteException sqlex)
{
MessageDialog dialog = new MessageDialog(sqlex.Message, "InsertMapRecord");
await dialog.ShowAsync();
}
}
应该避免使用
async void
方法(事件处理程序除外).
async void
methods should be avoided (except for event handlers).
然后在事件处理程序中,您应等待 InsertMapRecord
方法和任何其他异步方法:
In your event handler you should then await the InsertMapRecord
method and any other async method:
if (result == ContentDialogResult.Primary)
{
mapName = txtbxMapName.Text;
mapNotes = txtbxMapNotes.Text;
defaultZoomLevel = cmbxCre8MapZoomLevels.SelectedIndex + 1;
await InsertMapRecord(mapName, mapNotes, defaultZoomLevel);
}
如果执行此操作,则应该能够捕获任何异常并进行进一步调查.
If you do this, you should be able to catch any exception and investigate further.
这篇关于如何防止一种异步方法垄断另一种方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!