Task.Factory.StartNew with async lambda and Task.WaitAll

寵の児 提交于 2020-01-02 23:50:31

问题


I'm trying to use Task.WaitAll on a list of tasks. The thing is the tasks are an async lambda which breaks Tasks.WaitAll as it never waits.

Here is an example code block:

List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
    using (dbContext = new DatabaseContext())
    {
        var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
        //do long cpu process here...
    }
}
Task.WaitAll(tasks);
//do more stuff here  

This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?


回答1:


Task.Factory.StartNew doesn't recognise async delegates as there is no overload that accepts a function returning a Task.

This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run here:

tasks.Add(Task.Run(async () => ...



回答2:


This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?

The reason Task.WaitAll doesn't wait for the completion of the IO work presented by your async lambda is because Task.Factory.StartNew actually returns a Task<Task>. Since your list is a List<Task> (and Task<T> derives from Task), you wait on the outer task started by StartNew, while ignoring the inner one created by the async lambda. This is why they say Task.Factory.StartNew is dangerous with respect to async.

How could you fix this? You could explicitly call Task<Task>.Unwrap() in order to get the inner task:

List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
    using (dbContext = new DatabaseContext())
    {
        var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
        //do long cpu process here...
    }
}).Unwrap());

Or like others said, you could call Task.Run instead:

tasks.Add(Task.Run(async () => /* lambda */);

Also, since you want to be doing things right, you'll want to use Task.WhenAll, why is asynchronously waitable, instead of Task.WaitAll which synchronously blocks:

await Task.WhenAll(tasks);



回答3:


You can do like this.

    void Something()
    {
        List<Task> tasks = new List<Task>();
        tasks.Add(ReadAsync());
        Task.WaitAll(tasks.ToArray());
    }

    async Task ReadAsync() {
        using (dbContext = new DatabaseContext())
        {
            var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
            //do long cpu process here...
        }
    }



回答4:


you have to use the Task.ContinueWith method. Like this

List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(() =>
{
    using (dbContext = new DatabaseContext())
    {
        return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t =>
            {
                var records = t.Result;
                // do long cpu process here...
            });
        }
    }
}


来源:https://stackoverflow.com/questions/49650283/how-to-determine-the-task-which-using-await-inside-completed-or-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!