如何防止一种异步方法垄断另一种方法? [英] How can I prevent one async method from monopolizing another?

查看:70
本文介绍了如何防止一种异步方法垄断另一种方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆