5

I have some code that creates a task that does some slow work like this:

public static Task wait1()
{
    return new Task(() =>
    {
        Console.WriteLine("Waiting...");
        Thread.Sleep(10000);
        Console.WriteLine("Done!");
    });
}

In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:

public static Task wait2()
{
    return new Task(async () =>
    {
        Console.WriteLine("Waiting...");
        await Task.Delay(10000);
        Console.WriteLine("Done!");
    });
}

However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.

Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).

What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.

class Program
{
    static void Main(string[] args)
    {
        var w1 = wait1();
        w1.Start();
        w1.Wait(); // This waits 110 seconds

        var w2 = wait2();
        w2.Start();
        w2.Wait(); // This returns immediately
    }

    public static Task wait1()
    {
        return new Task(() =>
        {
            Console.WriteLine("Waiting...");
            Thread.Sleep(10000);
            Console.WriteLine("Done!");
        });
    }

    public static Task wait2()
    {
        return new Task(async () =>
        {
            Console.WriteLine("Waiting...");
            await Task.Delay(10000);
            Console.WriteLine("Done!");
        });
    }
}
4

4 回答 4

9

这似乎是不可能的!在此处查看alexm 的答案

异步方法返回的任务总是的,即它们是在运行状态下创建的。

:-(

我已经通过让我的代理排队Func<Task>s来解决这个问题,而接收任务的重载只是排队() => task。然后; 取消队列任务时,我检查它是否未运行,如果是,则启动它:

var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
    currentTask.Start();

必须这样做似乎有点笨拙(如果这个简单的解决方法有效;为什么对异步方法的原始限制总是被热创建?),但它似乎对我有用:-)

于 2013-07-07T17:55:37.370 回答
2

You could write this as:

public static async Task Wait2()
{
    Console.WriteLine("Waiting...");
    await Task.Delay(10000);
    Console.WriteLine("Done!");
}

In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).

Note that doing this will make it so you don't have to call Task.Start, as well.

于 2013-07-07T17:41:41.150 回答
1

为了帮助您理解这一点,请意识到 async / await 本质上不会创建新线程,而是将那部分代码安排在可用的时间点运行。

当您创建新的 Task(async () => ...) 时,您有一个运行异步方法的任务。当该内部异步方法遇到等待时,“新任务”被认为已完成,因为它的其余部分已被安排。为了帮助您更好地理解,在 await 命令之前的“新任务”中放置一些代码(如果需要,可以放置很多)。它将在应用程序终止之前全部执行,一旦到达等待,该任务将认为它已经完成。然后它返回并退出应用程序。

避免这种情况的最好方法是不要在任务中放置任何任务或异步方法。

从方法中删除 async 关键字和 await 关键字,它将按预期工作。

如果您熟悉,这与创建回调相同。

void MethodAsync(Action callback)
{
     //...some code
     callback?.Invoke();
}

//using this looks like this.
MethodAsync(() => { /*code to run when complete */});

//这是一样的

Task MethodAsync()
{
     //... some code here
}

//using it

await MethodAsync();
/*code to run when complete */

要理解的是,您基本上是在任务中创建一个新任务。所以内部的“回调”是在 await 关键字处创建的。

你的代码看起来像这样..

void MethodAsync(Action callback)
{
     //some code to run
     callback?.Invoke();  // <- this is the await keyword
     //more code to run.. which happens after we run whoever is
     //waiting on callback
}

显然缺少代码。如果这没有意义,请随时与我联系,我会提供帮助。async / await (旨在让事情变得更简单)是一开始会让你头疼的野兽。之后你得到它,那么它可能是你自 linq 以来在 c# 中最喜欢的东西。:P

于 2016-07-11T18:37:46.837 回答
0

尝试这个:

public async static Task wait2()
{
        Console.WriteLine("Waiting...");
        await Task.Delay(2000);
        Console.WriteLine("Done!");
}

但是我们知道任务已经开始,所以你不必调用 start:

var w2 = wait2();
//w2.Start();
w2.Wait(); 

我认为您的 wait2 函数的问题在于创建 2 个任务,一个 innew Task(...)另一个 in Task.Delay()。你在等待第一个,但你不是在等待内在的。

于 2013-07-07T17:43:22.637 回答