如何创建一个在主体内使用Wait的Task,该Task在调用Wait时的行为与同步版本相同

本文关键字:Wait Task 调用 版本 同步 创建 何创建 一个 主体 | 更新日期: 2023-09-27 18:21:17

我有一些代码创建了一个任务,该任务执行一些缓慢的工作,如

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

在实际实现中,Thread.Sleep实际上是一个web服务调用。我想更改方法的主体可以使用wait(这样它就不会在网络访问/睡眠期间消耗线程)。我的第一次尝试(基于霰弹枪调试编译错误)是这样的:

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

然而;这个任务的行为似乎与第一个任务不同,因为当我对它调用.Wet()时;它立即返回。

以下是显示差异的完整示例(控制台应用程序)(第二个任务开始时,应用程序将立即结束)。

我需要做什么才能对一个任务调用Start and Wait,而该任务内部恰好有使用Wait的代码?任务是排队的,稍后由代理执行,因此任务不能自动启动至关重要。

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

如何创建一个在主体内使用Wait的Task,该Task在调用Wait时的行为与同步版本相同

这似乎是不可能的!请点击此处查看alexm的答案:

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

:-(

我已经解决了这个问题,将我的代理队列改为Func<Task>s,而接收任务的重载只是将() => task排队。然后当取消任务时,我检查它是否没有运行,如果是,启动它:

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

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

您可以将其写成:

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

一般来说,使用new Tasknew Task<T>很少是个好主意。如果必须使用ThreadPool启动任务,而不是使用async/await语言支持来编写任务,则应使用Task.Run启动任务。这将安排任务的运行(这很重要,按照惯例,任务应该总是"热"的)。

请注意,这样做将使您不必调用Task.Start

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

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

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

从方法中删除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 */

需要理解的是,你基本上是在一个任务中创建一个新任务。因此,正在wait关键字处创建内部"callback"。

你的代码看起来是这样的。。

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

试试这个:

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个任务,一个在new Task(...)中,另一个在Task.Delay()中。你在等待第一个,但你没有等待内在的。