【问题标题】:Where to stop using async /await keywords?在哪里停止使用 async /await 关键字?
【发布时间】:2012-12-05 06:14:48
【问题描述】:

我的 DB2/400 有一个简单的心跳方法:

public bool CheckConnection()
        {
            try
            {
                using (OleDbConnection db = new OleDbConnection( this.conString ))
                {
                    OleDbCommand cmd = new OleDbCommand();
                    cmd.CommandText = "select 1 from sysibm.sysdummy1";
                    cmd.Connection = db;
                    db.Open();
                    cmd.ExecuteReader();
                    db.Close();
                    return true;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

我想在我的应用程序运行时使用它,当然我不想保留表单其余部分的执行。

我的表单中的主要方法:

public FrmMain()
    {
        InitializeComponent();
        PrepareFormTexts();
        PrepareFormData();
        PrepareStateClass();
        CheckDbConnection();
        //Initialize Data Access for AS400
        Dal = JDE8Dal.Instance;
        Dal.conString = Properties.Settings.Default.conAS400;
        //Initialize Icons
        picCon.Image = Properties.Resources.ledGreen;
        picStatus.Image = Properties.Resources.ledGreen;
        // Load recording from file if they exist
        PrepareRecordings(AppState.DataFileName,'|');
    }

CheckDbConnection 方法:

    private async Task<bool> CheckDbConnection()
    {
        return await Task.Factory .StartNew(() => Dal.CheckConnection());
    }

我认为它运行良好,但我收到警告

警告 1 由于不等待此调用,因此在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。

我应该忽略它吗?我应该将 async 放在 main 方法中吗?

更新: 在下面讨论后我发现,由于我使用 .NET 4.0 的异步包,我不能真正让我的 dal 方法成为非阻塞的。我唯一能做的就是使用 async/await + task.factory.startnew 以在 dal 在后台工作时让我的应用程序正常工作。

代码如下:

public FrmMain()
    {
        InitializeComponent();
        Load += FormLoadInit;
    }

    private async void FormLoadInit(object sender, EventArgs e)
    {
        PrepareFormTexts();
        PrepareFormData();
        PrepareStateClass();
        txtLot.Select();
        //Initialize Data Access for AS400
        Dal = JDE8Dal.Instance;
        Dal.conString = Properties.Settings.Default.conAS400;
        // Load recording from file if they exist
        PrepareRecordings(AppState.DataFileName, '|');
        bool cn = await Task.Factory.StartNew(() => Dal.CheckConnection());
        //Initialize Icons
        picCon.Image = cn ? Resources.ledGreen : Resources.ledRed;
        picStatus.Image = Properties.Resources.ledGreen;

    }

【问题讨论】:

  • 有没有实际使用CheckDbConnection的结果的场合?
  • 你检查过这个link吗?
  • @MarcGravell 抱歉:main 方法还没有准备好..当然我会在那里使用它来设置各种启动参数..放它看看它是否编译

标签: c# .net multithreading


【解决方案1】:

警告告诉您的是,您实际上是在触发然后忘记了这个异步任务。您从不实际使用操作的结果,甚至不存储 Task 本身以允许任何未来的代码依赖它的结果(甚至知道它何时完成)。

这不会破坏任何东西,但也不是很有帮助。您也可以不调用该方法。现在,就您而言,这只是一个虚拟列表,因此您需要问自己要测试什么。您想在表单上显示一些虚拟值作为概念证明吗?如果是这样,您的代码将需要进行一些更改才能到达那里。

要实际使用查询的结果,您需要await 它,而您不能在构造函数中这样做。您需要将代码移动到 Form Load 事件处理程序并将处理程序标记为 async,以便您可以在其中使用 await

您遇到的另一个问题是,要创建异步方法,您将启动一个将在线程池中运行的新任务,然后让它执行阻塞方法。使用 async/await 模型的主要优点是您不需要这样做。您应该使用直接为您提供Task&lt;T&gt; 作为返回值且不会阻塞的数据库查询方法。然后,您可以在这些任务上 await(或仅返回它们),这样您的应用程序在您等待时没有线程被阻塞。

您的CheckConnection 方法应该如下所示:

public async Task<bool> CheckConnection()
{
    try
    {
        using (OleDbConnection db = new OleDbConnection(this.conString))
        {
            OleDbCommand cmd = new OleDbCommand();
            cmd.CommandText = "select 1 from sysibm.sysdummy1";
            cmd.Connection = db;
            db.Open();
            await cmd.ExecuteReaderAsync();
            return true;
        }
    }
    catch (Exception)
    {
        return false;
    }
}

CheckDbConnection 然后不需要存在。无需将CheckConnection 包装在Task 中,因为它已经返回了一个任务。

【讨论】:

  • 所以你说我应该在加载事件中移动方法的调用,我明白了。但是接下来如果我不想直接从 dal 调用该方法,而是让业务层为我做这件事,那么 await /asyncs 的顺序是什么?我很困惑我需要将一对 asyncs/await 放在哪里。在 3 层方法中我在哪里有 await/async?
  • @e4rthdog DAL 应该公开一些返回TaskTask&lt;T&gt; 的方法。然后 UI 可以在该任务上await 执行非阻塞等待,直到该操作的结果完成。任何在其主体内包含await 的方法都需要在其签名中包含asyncasync 除了告诉编译器“嘿,我在这里某处有一个等待”之外什么也没做。如果有人将变量/方法/任何东西命名为await,这是一种确保向后兼容性的方法。
  • 如果我想正常使用dal方法怎么办,即使我将它返回为Task,我应该能够o它吗?同样在具体示例中,我只希望数据库检查方法在背景上运行,并在完成时根据结果将图片变为红色或绿色。同时我希望表格可以操作...
  • @e4rthdog 是的,即使它直接返回任务而不是布尔值,您也可以使用该方法。 DAL 方法“链接”它们的 async/await 运算符是完全可以接受的。如果一个复杂的操作使用更简单的 DAL 方法,它可以将自己标记为asyncawait 更简单的方法,因此它也会返回一个Task,并且代码可以写成顺序的,而不是异步的(即使不是,这很酷)。
  • 我尝试将我的 sal 方法更改为 Task 但我现在不能使用“return true”,我不知道我必须返回什么。而且,如果我希望应用程序继续其操作,在等待之后我将只有我需要在结果之后操作的代码。 init 的其余部分应该转到调用方法,以便在等待之后立即继续?
【解决方案2】:

构造函数不能是异步的(至少我每次尝试时总是会遇到编译器错误。但事件处理程序可以是。我认为大多数初始化代码无论如何都属于 Form.Load 处理程序。

public FrmMain()
{
    InitializeComponent();
    Load+=OnLoaded;
}

private async void Onloaded(object sender, EventArgs args)
{
    PrepareFormTexts();
    PrepareFormData();
    PrepareStateClass();
    await CheckDbConnection();
    //Initialize Data Access for AS400
    Dal = JDE8Dal.Instance;
    Dal.conString = Properties.Settings.Default.conAS400;
    //Initialize Icons
    picCon.Image = Properties.Resources.ledGreen;
    picStatus.Image = Properties.Resources.ledGreen;
    // Load recording from file if they exist
    PrepareRecordings(AppState.DataFileName,'|');
}

【讨论】:

  • 理论说当到达等待行时,控件将被移动到调用方法,当它完成时它将在该行之后恢复......如果这是真的,我真正想做的是检查数据库连接,同时继续我的其他工作......
【解决方案3】:
public FrmMain()
{
    InitializeComponent();
    PrepareFormTexts();
    PrepareFormData();
    PrepareStateClass();
    Task delayTask = CheckDbConnection();
    //Initialize Data Access for AS400
    Dal = JDE8Dal.Instance;
    Dal.conString = Properties.Settings.Default.conAS400;
    //Initialize Icons
    picCon.Image = Properties.Resources.ledGreen;
    picStatus.Image = Properties.Resources.ledGreen;
    // Load recording from file if they exist
    PrepareRecordings(AppState.DataFileName,'|');
}

【讨论】:

  • 这将使警告消失,但它仍然没有解决警告的含义,甚至没有对任务做任何事情。这实际上与添加警告抑制相同。
【解决方案4】:

为了完成你的范围,也许你应该考虑Reactive Extensions Framework。以下是基于您的问题示例的示例实现。您需要在表单中添加标签(label1label2)。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

//get Reactive Extensions and reference them in your project.
//Reactive Extensions for .NET (Rx .NET) http://msdn.microsoft.com/en-us/data/gg577610
using System.Reactive.Linq;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
        }

        async void Form1_Load(object sender, EventArgs e)
        {
            //do independent stuff
            PrepareSomeStuff();

            //notify the DB checking
            label1.Text = "Checking DB";

            //declare an IObservable
            IObservable<bool> startAsync = Observable.Start<bool>(() => CheckConnection()).FirstAsync<bool>();

            //do some other independent stuff
            PrepareSomeOtherStuff();

            //wait for the IObservable to return data
            bool isDbReady = await startAsync;
            label1.Text = "Ready";
        }

        private void PrepareSomeOtherStuff()
        {
            //do some other stuff
            label2.Text += "\r\nDo some other stuff";
        }

        private void PrepareSomeStuff()
        {
            //do stuff
            label2.Text = "Do stuff";
        }

        private bool CheckConnection()
        {
            //do stufff
            System.Threading.Thread.Sleep(5000);
            return true;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2017-05-19
    • 2015-05-04
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多