【问题标题】:Threads with async Task functions C#/.NET / await not compiling具有异步任务功能的线程 C#/.NET/等待未编译
【发布时间】:2017-11-27 13:51:44
【问题描述】:

我有一个项目,我需要多个项目列表的数据。对于每个项目,我都会调用一个 api 来获取这些信息。循环工作,虽然它需要 4 到 5 分钟才能完成(这是很多)。

以前的代码是这样的:

foreach (var project in projects)
{
    string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
    HttpWebRequest request;
    request = (HttpWebRequest)WebRequest.Create(url);
    request.Accept = "application/json";
    request.ContentType = "application/json";
    var executions = new Execs();

    var response = (HttpWebResponse)(await request.GetResponseAsync());

    using (response)
    {
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            JavaScriptSerializer js = new JavaScriptSerializer();
            var objText = reader.ReadToEnd();
            executions = (Execs)js.Deserialize(objText, typeof(Execs));
        }
    }


    execs.AddRange(executions.executions);
} 

为了提高性能,我认为使用线程可能是个好主意。所以,我想出了这样的事情:

ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = projects.Count;
foreach (var project in projects)
{
    new Thread(() =>
    {

        string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
        HttpWebRequest request;
        request = (HttpWebRequest)WebRequest.Create(url);
        request.Accept = "application/json";
        request.ContentType = "application/json";
        var executions = new Execs();

        var response = (HttpWebResponse)(await request.GetResponseAsync());

        using (response)
        {
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                JavaScriptSerializer js = new JavaScriptSerializer();
                var objText = reader.ReadToEnd();
                executions = (Execs)js.Deserialize(objText, typeof(Execs));
            }
        }

        lock (execs)
        {
            execs.AddRange(executions.executions);
        }

        if (Interlocked.Decrement(ref toProcess) == 0)
            resetEvent.Set();
    }).Start();

}

这段代码的问题在于:

var response = (HttpWebResponse)(await request.GetResponseAsync());

从我添加Thread 的那一刻起就不再编译了。我得到的错误是

"'await' 运算符只能在异步 lambda 表达式中使用"

当我不使用线程时,这不是问题。 GetResponseAsync 是一个async 函数,必须使用await。我尝试删除它(我同意这不合逻辑,但我用完了选项)但编译器告诉我我需要一个 await 来获得 async 函数。 我不太明白Thread 的实现有什么变化。

我没有正确机械地使用螺纹吗?我应该怎么做才能纠正这个问题或实现我想要正确做的事情?

【问题讨论】:

  • 要修复编译器错误,您需要将异步 lambda 传递给线程构造函数:new Thread(async () => ...
  • 与显式启动线程相比,您将从 async / await 和 Task.WhenAll 中获得更多收益。 (并且优先考虑任务而不是线程 - 不太可能在脚上开枪)。
  • 如果你想使用Task而不是Thread来分割Taskproject,只需使用projects.Select(async project => ...
  • 线程太贵了,你也不希望任何时候有太多的线程/套接字。
  • 而@StuartLC 提供了一个很好的答案。如果您打算继续沿 async 路径前进,那么值得研究一下 HttpClient,它有大量直接的 async 支持。

标签: c# .net multithreading asynchronous async-await


【解决方案1】:

您正在混合多种编码范式,即async / await,以及老式线程启动和同步,这可能会导致麻烦。

按照上面的cmets

  • 您的代码无法编译的原因是您试图在传递给线程的同步代码中使用await。您也可以将 lambda 限定为 async
  • Task 是比 Thread 更安全的范例,TPL 提供了丰富且富有表现力的工具来辅助异步和并行。
  • 如果您单独处理每个并行任务,而不共享任何数据(例如您锁定的集合),而是从每个任务返回结果数据,您可以使用 LINQ 在线程安全中整理结果方式。

var myTasks = projects.Select(async project =>
{
    var url = $"urlOneProject{project.name}{secondPartUrl}?authtoken={authToken}";
    var request = (HttpWebRequest) WebRequest.Create(url);
    request.Accept = "application/json";
    request.ContentType = "application/json";

    using (var response = (HttpWebResponse) (await request.GetResponseAsync()))
    using (var reader = new StreamReader(response.GetResponseStream()))
    {
        var objText = await reader.ReadToEndAsync();
        return JsonConvert.DeserializeObject<Execs>(objText);
    }
});

var execs = (await Task.WhenAll(myTasks))
    .SelectMany(result => result.executions);

其他说明

  • 不要使用 JavaScriptSerializer - 即使 MSDN docco 说要使用 NewtonSoft Json
  • 我已经包含了 reader.ReadToEndAsync 的异步版本。
  • 您可以取消锁定和ManualResetEvent - 因为每个任务都会返回它的结果,我们将把它留给Task.WhenAll 来整理数据。
  • 您可以使用SelectMany 展平多个executions 的孩子
  • 相邻的 using 子句是可堆叠的 - 它可以减少缩进时的眼睛疲劳。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-13
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多