异步功能作为同步调用

时间:2015-03-01 03:58:21

标签: c# asp.net async-await

以下是我正在使用的代码。旨在使其成为对耗时功能的异步调用。

async Task<DataSet> GetDataAsync()
    {
        System.Threading.Thread.Sleep(5000);
        SqlDataAdapter adpator = new SqlDataAdapter("Select * from table1;select * from table2;select * from table3;select * from table4"
            , @"Data Source=ANKIT\SQLEXPRESS;Initial Catalog=IM_DB;Integrated Security=True");
        DataSet ds = new DataSet();
        adpator.Fill(ds);
        return ds;
    }

    protected async void btnFillData_Click(object sender, EventArgs e)
    {
        lblStatus.Text = "Going to run a blocking thread....";
        Task<DataSet> dsAsync = GetDataAsync();
        lblStatus.Text = "Going to await the same......";
        DataSet ds = await dsAsync;
        lblStatus.Text = "released from await";
        gvIngredient.DataSource = ds.Tables[0];
        gvIngredient.DataBind();
    }

功能 GetDataAsync 作为同步调用。我希望它是异步的,因为它正在进行数据库调用以获取数千条记录,这非常耗时。

我在asp.net的异步模型中遗漏了一些东西。

2 个答案:

答案 0 :(得分:2)

第一个问题:

修饰符async不会使您的方法异步,它只是让您有机会在里面使用await关键字。因此,函数GetDataAsync不是异步的 - 它使用与调用它相同的线程。要使此函数异步,您必须使用Task类和StartNew方法,如下所示:

Task<DataSet> GetDataAsync()
{
    Function<DataSet> func = () =>
    {
        System.Threading.Thread.Sleep(5000);
        SqlDataAdapter adpator = new SqlDataAdapter("Select * from table1;select * from table2;select * from table3;select * from table4"
               , @"Data Source=ANKIT\SQLEXPRESS;Initial Catalog=IM_DB;Integrated Security=True");
        DataSet ds = new DataSet();
        adpator.Fill(ds);
        return ds;
    };

    return Task.Factory.StartNew(func);
}

检查this非常有用的文章,了解async\await的工作原理。

第二个:

每次调用SqlDataAdapter后,您的代码会将GetDataAsync保留在内存中。此行为导致内存泄漏。您最好使用using声明:

DataSet ds = new DataSet();
using(var adapter = new SqlDataAdapter(query, connectionString))
    adapter.Fill(ds);

希望这有帮助。

答案 1 :(得分:0)

  

功能GetDataAsync作为同步调用。

是。事实上,有一个编译器警告会告诉你GetDataAsync将同步运行。

可以使其异步,但不能使用DataSet.Fill方法。这是一种很老的做事方式,并没有更新为使用新的异步API。您必须使用类似Entity Framework 6或异步DbCommands的东西。

但是在你走这条路之前......

protected async void btnFillData_Click(object sender, EventArgs e)
{
  lblStatus.Text = "Going to run a blocking thread....";
  Task<DataSet> dsAsync = GetDataAsync();
  lblStatus.Text = "Going to await the same......";
  DataSet ds = await dsAsync;
  lblStatus.Text = "released from await";
  gvIngredient.DataSource = ds.Tables[0];
  gvIngredient.DataBind();
}

此代码显示了对ASP.NET如何工作的误解。单击该按钮时,会向ASP.NET服务器发送一个HTTP请求,并且服务器只能发送一个HTTP响应。此响应仅在click事件方法的 end 处发送。因此,多次设置lblStatus.Text不会产生任何影响 - 用户只能看到最后一次更新。

换句话说,ASP.NET上的await只会产生ASP.NET运行时;它不会屈服于客户端(浏览器)。在所有await完成之前,不会发送HTTP响应。

那么,你真正询问的是如何启动在ASP.NET中长时间运行的操作,然后让客户端稍后更新 有进度或完成信息。为此,您需要一种不同的技术,例如SignalR,AJAX或UpdatePanel