任务.ContinueWith在任务完成之前触发。

7

我想在为任务注册继续操作后启动它。但是,在调用await Task.Delay()之后,继续操作会立即触发。

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

输出:

Before delay
ContinueWith
After delay

这里出了什么问题?

2
new Task( -> new Task<Task>(task.ContinueWith -> task.Unwrap().ContinueWith - user4003407
2
除非您正在编写任务调度程序,否则几乎没有理由使用 new Task(。请改用 Task.Run( - Scott Chamberlain
我正在尝试创建一个任务,当它完成时会从任务列表中移除自己。为了确保安全,我需要在启动任务之前确保它已被添加到列表中。我正在尝试执行以下操作:创建一个任务,将其添加到列表中,注册一个继续操作以将其删除,然后启动该任务。是否有更好的方法? - Poma
3
你遇到问题的部分不是这个。问题在于连续执行操作被连接到了错误的任务上,因为你不理解async的工作原理。一旦遇到第一个await,你的任务就会立即结束,因为内部任务(等待Task.Delay的任务)和外部任务(你用new Task明确创建的任务)之间没有连接。正如Scott所说的那样,“不要使用任务构造函数”。代码将使用Task.Run正常工作(但你实际上也不应该使用它 - 只需运行代码,无需将其提交到另一个线程中并立即返回)。 - Luaan
2个回答

3
你的问题 - 正如其他人所指出的 - 是因为 Task.Task 不理解 async 委托。正如我在博客中描述的那样,永远不应该使用 Task 构造函数 - 它实际上没有任何用例。
如果你想在线程池线程上运行代码,请使用 Task.Run
另外,你 不应该使用 ContinueWith;它是一个极低级和危险的 API(正如我在博客中所描述的)。相反,你应该使用 await
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();
    }
}

我有一个循环,它启动任务并将它们放入列表中。我希望任务在完成后从此列表中删除自己。但是当前任务没有对自身的引用(至少我认为没有)。这就是为什么我使用 .ContinueWith 来删除它们的原因。 - Poma
@Poma:你仍然应该使用await。使用ContinueWith并忽略返回的任务与在async方法中使用await并忽略返回的任务大致相同(除了await具有更合理的默认行为)。 - Stephen Cleary
@StephenCleary 我已经为了安排我的情况而苦恼了一段时间 :( public async Task<TenantDto> CreateAsync(CreateTenantDto input) { try { TenantDto newTenantDto = null; using (var uow = _unitOfWorkManager.Begin(new AbpUnitOfWorkOptions { IsTransactional = true })) { --插入 #1(父实体) uow.OnCompleted(async () => { --插入 #2(子实体) }); await uow.CompleteAsync(); return newTenantDto; // 是 null,因为 OnCompleted 还没有被调用! } } ... } - Alexander
@Alexander:请提出您自己的问题;在评论中阅读代码很困难。 - Stephen Cleary
@StephenCleary 请看一下!https://stackoverflow.com/questions/63069170/cannot-insert-two-entities-using-3rd-party-unitofwork-component 谢谢! - Alexander

-1

使用 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

输出:

延迟前
延迟后
继续执行

为什么你的代码不起作用: 看看你的示例(稍作修改):

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
}

您可以获取对outerTask的引用。 outerTask与innerTask没有任何关联。 调用start后,outerTask触发委托的执行,并立即使用“ContinueWith”委托继续执行。 连续性连接到outerTask!

OP的评论:

我正在尝试创建一个任务,在完成时从任务列表中删除自己。为了安全起见,我需要确保在启动任务之前将其添加到列表中。我正在尝试执行以下操作:创建任务,将其添加到列表中,注册删除它的连续性,然后启动任务。有更好的方法吗?

虽然以下代码有效,但您必须首先证明使用的合理性! 该代码未经过优化或其他处理。肯定有更好的模式。请进行研究。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接