Task.ContinueWith fires before task is finished

浪尽此生 提交于 2021-02-08 21:49:12

问题


I'm trying to start a task after I register a continuation for it. But continuation fires immediately after await Task.Delay() is called.

using System;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApplication30
{
    class Program
    {
        static void Main(string[] args)
        {
            var task = new Task(async delegate {
                Console.WriteLine("Before delay");
                await Task.Delay(1000);
                Console.WriteLine("After delay");
            });

            task.ContinueWith(t => {
                Console.WriteLine("ContinueWith");
            });

            task.Start();

            Console.ReadLine();
        }
    }
}

Output:

Before delay
ContinueWith
After delay

What is wrong here?


回答1:


Your problem - as others have noted - is that Task.Task does not understand async delegates. As I describe on my blog, the Task constructor should never be used - it has literally zero use cases.

If you want to run code on a thread pool thread, use Task.Run.

Also, you shouldn't use ContinueWith; it's an extremely low-level and dangerous API (as described on my blog). You should use await instead.

class Program
{
    static void Main(string[] args)
    {
        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        var task = Task.Run(async delegate {
            Console.WriteLine("Before delay");
            await Task.Delay(1000);
            Console.WriteLine("After delay");
        });

        await task;
        Console.WriteLine("await");

        Console.ReadLine();
    }
}



回答2:


Use Task.Run

void Main()
{
    Task.Run(async()=>{
                Console.WriteLine("Before delay");
                await Task.Delay(1000);
                Console.WriteLine("After delay");
            }).ContinueWith(t => {
                // do somthing with your Task t here
                Console.WriteLine("ContinueWith");
            });

           // task.Start();

            Console.ReadLine();
}

// Define other methods and classes here

Output:

Before delay
After delay
ContinueWith

Why your code doesn't work: Look at your example (a bit modified):

static void Main(string[] args)
{
    var outerTask = new Task(async delegate {
        Console.WriteLine("Before delay");
         await Task.Delay(1000); // inner task
        Console.WriteLine("After delay");
    },"Outertask");

    outerTask.ContinueWith(t => {
        Console.WriteLine("ContinueWith");
    });

    outerTask.Start();
    outerTask.Wait(); // wait for outerTask to finish
    var breakHere = 0; // set a brakpoint here
}

You get a reference to the outerTask. The outerTask has no connection to the innerTask. After calling start, the outerTasks triggers execution of the delegate and continues immediately with "ContinueWith" delegate. The continuation is connected to the outerTask!

OP's comment:

I'm trying to make a task that removes itself from task list when it finishes. To do it safely I need to ensure that it is added to the list before it is started. I'm trying to do the following: create a task, add it to the list, register continuation that removes it, start the task. Is there a better way?

While the following code works, you have to justify the usage first! The code is not optimized or whatever. Ther are or for sure better patterns out there. Do your research.

ConcurrentBag<AsyncLazy<Task>> taskList = new ConcurrentBag<AsyncLazy<Task>>();
void Main()
{
    int v = 3242;
    AsyncLazy<Task> objAsyncLazy = null;
    objAsyncLazy = new AsyncLazy<Task>(new Func<Task<Task>>(async () =>
   {
       return await Task.FromResult<Task>(doLongRunningAsync(v).
                    ContinueWith<Task>(async (doLongRunningAsyncCompletedTask) =>
                   {
                       Console.WriteLine(doLongRunningAsyncCompletedTask.IsCompleted); // 
                       await removeMeFromListAsync(objAsyncLazy);
                   }));
   }));

    taskList.Add(objAsyncLazy);
    Console.WriteLine("al added");
    Console.WriteLine("ConcurrentBag.Count: " + taskList.Count);
    // execute the task
    var t = objAsyncLazy.GetValueAsync(System.Threading.CancellationToken.None);

    // wait for the first task "t" to finish or not, 
    // ContinueWith will execute after first task "t" has finished anyways. 
    t.Wait(); 
    // ContinueWith is executing now
    Console.ReadLine();
}

public async Task doLongRunningAsync(int val)
{
    Console.WriteLine("Before delay" + val);
    await Task.Delay(1000);
    Console.WriteLine("After delay");
}

public async Task removeMeFromListAsync(AsyncLazy<Task> al) //(AsyncLazy<Task> t)
{
    Console.WriteLine("continue start");
    taskList.TryTake(out al);
    Console.WriteLine("al removed");
    Console.WriteLine("ConcurrentBag.Count: " + taskList.Count);
    await Task.Delay(1000);
    Console.WriteLine("continue end");
}
}


来源:https://stackoverflow.com/questions/39192867/task-continuewith-fires-before-task-is-finished

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