我有一些代码创建了一个任务,该任务执行一些缓慢的工作,如
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!");
});
}
}
这似乎是不可能的!请点击此处查看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 Task
或new 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()
中。你在等待第一个,但你没有等待内在的。