Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 等待任务在ASP.net中从未完成,但在控制台应用程序中完成_C#_Asp.net_Asp.net Mvc_Multithreading_Task - Fatal编程技术网

C# 等待任务在ASP.net中从未完成,但在控制台应用程序中完成

C# 等待任务在ASP.net中从未完成,但在控制台应用程序中完成,c#,asp.net,asp.net-mvc,multithreading,task,C#,Asp.net,Asp.net Mvc,Multithreading,Task,我需要一种方法来减少一些报告查询的额外时间,并选择使用异步方法并行执行一些任务。我对异步非常陌生,所以在将其插入asp.net MVC应用程序之前,我制作了一个测试控制台应用程序来验证这个概念。在控制台应用程序中,一切都按预期工作,但在asp.net中,相同的代码永远不会通过“whenall”。这是我的密码: //Synchronous call for outside world public DataSet GetQueries_Sync() { Task<DataSet>

我需要一种方法来减少一些报告查询的额外时间,并选择使用异步方法并行执行一些任务。我对异步非常陌生,所以在将其插入asp.net MVC应用程序之前,我制作了一个测试控制台应用程序来验证这个概念。在控制台应用程序中,一切都按预期工作,但在asp.net中,相同的代码永远不会通过“whenall”。这是我的密码:

//Synchronous call for outside world
public DataSet GetQueries_Sync()
{
    Task<DataSet> Out = GetQueries();
    Out.Wait();
    return Out.Result;
}

//Run all needed tasks in parallel
private async Task<DataSet> GetQueries()
{
    Task<DataTable> Task1 = QueryOne();
    Task<DataTable> Task2 = QueryTwo();
    Task<DataTable>[] Tasks = new Task<DataTable>[] { Task1, Task2 };
    await Task.WhenAll(Tasks);
    DataSet Out = new DataSet();
    Out.Tables.Add(Task1.Result);
    Out.Tables.Add(Task2.Result);
    return Out;
}

//Individual Queries
private string ConnString = "MyConnectionString";
private Task<DataTable> QueryOne()
{
    return Task.Run(() =>
    {
        DataTable Out = new DataTable();
        string SQL = "";
        SqlConnection Conn = new SqlConnection(ConnString);
        SqlDataAdapter Adapter = new SqlDataAdapter(SQL, Conn);
        Conn.Open();
        Adapter.Fill(Out);
        Out.TableName = "QueryOne";
        Conn.Close();
        return Out;
    });
}

private Task<DataTable> QueryTwo()
{
    return Task.Run(() =>
    {
        DataTable Out = new DataTable();
        string SQL = "SQL Statement #2, ~30sec";
        SqlConnection Conn = new SqlConnection(ConnString);
        SqlDataAdapter Adapter = new SqlDataAdapter(SQL, Conn);
        Conn.Open();
        Adapter.Fill(Out);
        Out.TableName = "QueryTwo";
        Conn.Close();
        return Out;
    });
}
//对外部世界的同步调用
公共数据集GetQueries\u Sync()
{
Task Out=getquerys();
出去,等等;
返回结果;
}
//并行运行所有需要的任务
私有异步任务GetQueries()
{
任务Task1=QueryOne();
任务Task2=QueryTwo();
任务[]任务=新任务[]{Task1,Task2};
等待任务。何时(任务);
数据集输出=新数据集();
Out.Tables.Add(Task1.Result);
Out.Tables.Add(Task2.Result);
返回;
}
//个别查询
私有字符串ConnString=“MyConnectionString”;
私有任务QueryOne()
{
返回任务。运行(()=>
{
DataTable Out=新的DataTable();
字符串SQL=“”;
SqlConnection Conn=新的SqlConnection(ConnString);
SqlDataAdapter=新的SqlDataAdapter(SQL,Conn);
Conn.Open();
3.填写;
Out.TableName=“QueryOne”;
康涅狄格州关闭();
返回;
});
}
私有任务查询two()
{
返回任务。运行(()=>
{
DataTable Out=新的DataTable();
string SQL=“SQL语句#2,~30秒”;
SqlConnection Conn=新的SqlConnection(ConnString);
SqlDataAdapter=新的SqlDataAdapter(SQL,Conn);
Conn.Open();
3.填写;
Out.TableName=“QueryTwo”;
康涅狄格州关闭();
返回;
});
}

在asp.net(.net Framework 4.7)中,即使每个查询函数都返回其结果,GetQueries()中的“Task.WhenAll(Tasks)”也不会运行。控制台应用程序基本相同,只是方法是静态的。当所有(任务)在控制台环境中按预期继续时。你知道为什么相同的代码可以在控制台应用程序中工作,但不能在asp.net应用程序中工作吗?

为什么在asp.net中发生这种情况而不能在控制台应用程序中发生,这是因为在从异步上下文切换到同步上下文时,前者在幕后是如何工作的。。。简而言之,当你试图做这样的事情时,你会遇到僵局

有一些方法可以缓解这种行为(ConfigureAwait),但是当你走这条路的时候,你必须小心如何链接/管理线程等等。。。这就是为什么它通常不被推荐

您可以在此处阅读更多关于此的信息:


请注意,ASP.Net内核不会发生这种情况。

有两种方法可以等待异步任务完成

task.Wait();
task.Result;
task.GetAwaiter().GetResult();
阻塞。以下操作将阻止当前线程并等待任务完成

task.Wait();
task.Result;
task.GetAwaiter().GetResult();
非阻塞。以下内容将该方法一分为二,并将其剩余部分(称为“continuation”)发布到同步上下文中。然后,它将控制权和表示继续的任务一起返回给调用方。调用方可以通过使用另一个wait递归地等待任务来强制继续完成

await task;
对于开发人员来说,控制流看起来非常相似,但实际上它们在什么线程运行什么代码方面非常相似。差异取决于具体的应用程序类型,而具体的应用程序类型因应用程序类型而异。例如,ASP.NET使用,而WinForms应用程序使用

在控制台应用程序中,同步上下文只是线程池,是自由线程。因此,您可以使用任何一种技术,而不会出现死锁。即使当前线程被阻塞,延续也将能够执行,因此
Wait()
Result
,和
GetResult()
最终将返回

在ASP.NET应用程序中,同步上下文设计为允许在单个线程上执行(不完全是因为,但您可以单独阅读)。这允许您编写ASP.NET代码,而无需担心锁定或竞争条件(大多数情况下)。但这也意味着阻塞当前线程将阻止继续运行。由于当前线程在继续完成之前一直处于阻塞状态,并且无法执行继续,因此会出现死锁情况
Wait()
Result
GetResult()
将永远不会返回,并且应用程序将挂起

通过使用
wait
确保只等待任务完成,可以避免此问题。但是,这意味着该方法必须标记为异步

如果无法将方法标记为异步,则必须使用一种变通方法,这比您想象的要复杂得多。看看如何学习


另请参见和。

您使用的
.Wait()
.Result
。这些是代码气味。您需要等待结果,并将该方法标记为async,然后尽可能返回一个任务(或多个任务),直到调用堆栈的最上层。看见您需要更改
GetQueries\u Sync
及其调用方式。因此,您显示工作代码并询问是否不工作?您的SqlConnection处理不好。您需要遵循正确的模式。@塞尔文:代码是相同的,只是环境不同而已。此代码在控制台应用程序中有效,但在asp.net应用程序中无效。是否有任何异常?添加异常处理程序以确保代码不会因为并行运行两个SQL连接而获得异常。