简体   繁体   English

具有异步Task函数C#/。NET的线程/等待未编译

[英]Threads with async Task functions C#/.NET / await not compiling

I have a project where I need multiple data for a list of projects. 我有一个项目,我需要多个数据才能获得项目列表。 For each project I call an api to get me this information. 对于每个项目,我都调用一个api来获取此信息。 The loop works, although it takes 4 to 5 minutes to finish(Which is A LOT). 尽管需要4到5分钟才能完成循环(虽然很多),但循环仍然有效。

The code used to look like this : 以前的代码看起来像这样:

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);
} 

To improve performance I thought that using threads might be a good idea. 为了提高性能,我认为使用线程可能是一个好主意。 So, I came up with something like this: 所以,我想到了这样的东西:

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();

}

The problem with this code is that the line: 此代码的问题是该行:

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

doesn't compile anymore from the moment I added Thread . 从我添加Thread的那一刻起,它就不再编译了。 And the error I get is 我得到的错误是

"The 'await' operator can only be used within an async lambda expression " “'await'运算符只能在异步lambda表达式中使用”

That wasn't a problem when I didn't use threads. 当我不使用线程时,这不是问题。 The GetResponseAsync is an async function and the use of the await is compulsory. GetResponseAsyncasync函数,必须使用await I tried deleting it (which wasn't logical I agree but I ran out of options) but the compiler tells me I need an await for an async function. 我尝试删除它(我同意这是不合逻辑的,但是我用尽了所有选项),但是编译器告诉我我需要await async功能。 I don't quite understand what changes with the implementation of the Thread . 我不太了解Thread的实现会发生什么变化。

Didn't I use the threads mechanically correctly? 我没有正确地机械地使用螺纹吗? What should I do to correct this or to implement what I want to do correctly? 我该怎么做才能纠正此问题或正确执行我想做的事情?

You are mixing multiple paradigms of coding, viz async / await , and oldschool Thread starting and synchronization, which is likely to lead to trouble. 您正在混合使用多种编码范例,即async / await和oldschool线程启动和同步,这可能会导致麻烦。

As per the above comments 根据以上评论

  • The reason your code doesn't compile is because you are attempting to use await in otherwise synchronous code passed to the thread. 您的代码无法编译的原因是,您试图在传递给线程的同步代码中使用await You can qualify lambdas as async as well. 您也可以将lambda限定为async
  • Task is a much safer paradigm than Thread , and TPL provides rich and expressive tools to assist in asynchrony and parallelism. Task是比Thread更安全的范例,而TPL提供了丰富而富有表现力的工具来辅助异步和并行性。
  • If you process each parallel task in isolation, without sharing any data (such as the collections that you are locking), but instead return the resultant data from each Task you can then use LINQ to collate the results in a thread-safe manner. 如果您独立处理每个并行任务,而不共享任何数据(例如您要锁定的集合),而是从每个任务返回结果数据,则可以使用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);

Other notes 其他注意事项

  • Don't use JavaScriptSerializer - even the MSDN docco says to use NewtonSoft Json 不要使用JavaScriptSerializer甚至MSDN docco都说要使用NewtonSoft Json
  • There's an async version of reader.ReadToEndAsync which I've included. 我已经包含了reader.ReadToEndAsync的异步版本。
  • You can drop the locks and the ManualResetEvent - since each Task returns it's result, we'll leave it to Task.WhenAll to collate the data. 您可以放下锁和ManualResetEvent因为每个Task都会返回它的结果,所以我们将其留给Task.WhenAll来整理数据。
  • You can flatten the children of multiple executions with a SelectMany 您可以使用SelectMany来平化多个executions的子级
  • Adjacent using clauses are stackable - it saves a bit of eyestrain on the indentation. 相邻的using子句是可堆叠的-节省了缩进的视线。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM