c# async await行为不符合预期

本文关键字:不符合 async await | 更新日期: 2023-09-27 18:07:23

我有一个c#项目,它基本上由以下3个类组成。它应该异步读取本地数据库(sqlite)并继续主进程。虽然它不会抛出任何错误,也不会执行db读取。它只执行包含在主进程while(true) {"Doing Stuff on the Main Thread..."}中的无限循环。似乎主线程没有等待异步进程。提前感谢。

class DatabaseAccessor
{
    static void Main(string[] args)
    { 
        var inq = new InquireDatabaseAsync();
        inq.DoWork();
        while (true)
        {
            Console.WriteLine("Doing Stuff on the Main Thread...");
        }
    }      
}
public class InquireDatabaseAsync
{
    public async Task DoWork()
    {
        await Task.Run(() =>
        {
            LongRunningOperation();
        });
    }
    private static async Task LongRunningOperation()
    {
        Data_connection2 dbobject = new Data_connection2();
        SQLiteConnection m_dbConnection = new SQLiteConnection();
        m_dbConnection.ConnectionString = dbobject.datalocation2();
        m_dbConnection.Open();
        string sql = "SELECT * FROM Commands WHERE Id = (SELECT MAX(Id) FROM Commands)";
        SQLiteCommand SQLcommand = new SQLiteCommand(sql, m_dbConnection);
        var instruct = await ExecuteLoopTaskAsync(SQLcommand);
    }
    public async static Task<string> ExecuteLoopTaskAsync(SQLiteCommand sqlCommand)
    {          
        while (true)
        {
            System.Threading.Thread.Sleep(1000);
            var reader = sqlCommand.ExecuteReader();
            while (reader.Read())
                Console.WriteLine("Id: " + reader["Id"] + "'tInstruction: " + reader["Instruction"] + "'tCellular: " + reader["Cellular"] + "'tTimestamp: " + reader["Timestamp"]);
        }
        return "Finished...";
    }
}
class Data_connection2
{
    public string datalocation2()
    {
        String dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        return "Data Source=" + dir + "''database9.sqlite";
    }
}

c# async await行为不符合预期

这一行返回一个Task:

inq.DoWork();

但是没有对Task对象做过任何事情。因此,它异步启动任务,并且消费代码不会阻塞它。如果您确实想阻塞它(在这种情况下您可能会这样做),您可以等待任务:

inq.DoWork().Wait();

虽然内部工作可能比我解释的(或者我现在完全掌握的)更复杂,但这个有效的应该具有与使用

相同的观察行为:
await inq.DoWork();

但是由于封闭方法不是async,那么您不能使用该语法。


基本上考虑的范式,事情应该是"异步所有的方式"。这意味着在任何给定的异步调用堆栈的顶层应该有一些东西来管理Task。对于WinForms、WPF、ASP等技术。. NET等,有内置的机制来做到这一点。对于一款主机应用,你的设计基本上要简单得多。main()方法最顶层的方法,需要在堆栈上管理它下面的Tasks方法。

你可能想做的是,

static void Main(string[] args)
{
    var enquiry = new InquireDatabaseAsync().DoWork();
    while (!enquiry.IsCompleted)
    {
        Console.WriteLine("Doing Stuff on the Main Thread...");
    }
    // In case there were any exceptions.
    enquiry.Wait();
}      

这段代码有几个问题。考虑使用如下内容:

using System;
using System.Threading.Tasks;
namespace ConsoleApplication6
{
    class DatabaseAccessor
    {
        static async void Main(string[] args)
        {
            await Task.Run(() =>
            {
                InquireDatabaseAsync.LongRunningOperation();
            });
        }
    }
    public static class InquireDatabaseAsync
    {
        public static void LongRunningOperation()
        {
            Data_connection2 dbobject = new Data_connection2();
            SQLiteConnection m_dbConnection = new SQLiteConnection();
            m_dbConnection.ConnectionString = dbobject.datalocation2();
            m_dbConnection.Open();
            string sql = "SELECT * FROM Commands WHERE Id = (SELECT MAX(Id) FROM Commands)";
            SQLiteCommand SQLcommand = new SQLiteCommand(sql, m_dbConnection);
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                var reader = sqlCommand.ExecuteReader();
                while (reader.Read())
                    Console.WriteLine("Id: " + reader["Id"] + "'tInstruction: " + reader["Instruction"] + "'tCellular: " + reader["Cellular"] + "'tTimestamp: " + reader["Timestamp"]);
                break;
            }
            Console.WriteLine("Finished.");
        }
    }
    class Data_connection2
    {
        public string datalocation2()
        {
            String dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            return "Data Source=" + dir + "''database9.sqlite";
        }
    }
}

虽然DoWork在内部等待任务,但它立即返回给调用者!因此,如果调用者想要在继续之前等待任务终止,则必须再次等待。

inq.DoWork().Wait();