【问题标题】:Task.ContinueWith fires before task is finishedTask.ContinueWith 在任务完成之前触发
【发布时间】:2016-09-01 10:53:27
【问题描述】:

我在为它注册一个延续之后尝试开始一个任务。但是在调用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

这里有什么问题?

【问题讨论】:

  • new Task( -> new Task<Task>(,task.ContinueWith -> task.Unwrap().ContinueWith
  • 几乎没有理由使用new Task (,除非您正在编写任务调度程序。请改用Task.Run (
  • 我正在尝试创建一个在完成时将自身从任务列表中删除的任务。为了安全地做到这一点,我需要确保在启动之前将其添加到列表中。我正在尝试执行以下操作:创建一个任务,将其添加到列表中,注册删除它的延续,启动任务。有没有更好的办法?
  • 这不是你有问题的部分。问题是继续连接到错误的任务,因为您不了解async 的工作原理。您的任务在到达第一个await 后立即完成,因为内部任务(等待Task.Delay 的任务)和外部任务(您使用new Task 显式创建的)之间没有任何联系。正如 Scott 所说,不要使用任务构造函数。该代码可以与Task.Run 一起正常工作(而且您也不应该真正使用它 - 只需运行代码,无需将其发布到另一个线程即可立即返回)。

标签: c# async-await .net-4.5


【解决方案1】:

您的问题 - 正如其他人所指出的那样 - 是 Task.Task 不理解 async 代表。正如我在我的博客中所描述的,the Task constructor should never be used - 它实际上有 个用例。

如果您想在线程池线程上运行代码,请使用Task.Run

还有,你shouldn't use 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:你仍然应该使用await。使用ContinueWith 并忽略返回的任务与在async 方法中使用await 并忽略返回的任务大致相同。 (除了await有更合理的默认行为)。
  • @StephenCleary 我一直在努力整理我的案子 :( public async Task<TenantDto> CreateAsync(CreateTenantDto input) { try { TenantDto newTenantDto = null; using (var uow = _unitOfWorkManager.Begin(new AbpUnitOfWorkOptions { IsTransactional = true })) { --Insert #1 (parent entity) uow.OnCompleted(async () => { --Insert #2 (child entity) }); await uow.CompleteAsync(); return newTenantDto; // is null, because OnCompleted is not called yet! } } ... }
  • @Alexander:请提出你自己的问题; cmets中的代码很难阅读。
  • @StephenCleary 请看一看! stackoverflow.com/questions/63069170/…谢谢!
【解决方案2】:

使用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 的引用。 外层任务与内层任务没有任何联系。 调用 start 后,outerTasks 触发委托的执行,并立即使用“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");
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 2016-07-16
    相关资源
    最近更新 更多