C# 学习C&x27;s异步/等待/任务结构;代码挂起等待?
为了了解C#的线程构造是如何工作的,我编写了一个简单的测试程序,它执行相同的任务-睡眠3秒钟,然后返回一个对象-同步和异步。我设置了一些秒表,以便更好地了解代码是如何流动的 同步调用按预期工作:“同步前”和“同步后”打印之间有3秒的延迟。但是,对于异步调用,程序在“after async”之后无限期挂起。我希望“异步之前”和“异步之后”能够连续快速打印(因为C# 学习C&x27;s异步/等待/任务结构;代码挂起等待?,c#,.net,multithreading,asynchronous,async-await,C#,.net,Multithreading,Asynchronous,Async Await,为了了解C#的线程构造是如何工作的,我编写了一个简单的测试程序,它执行相同的任务-睡眠3秒钟,然后返回一个对象-同步和异步。我设置了一些秒表,以便更好地了解代码是如何流动的 同步调用按预期工作:“同步前”和“同步后”打印之间有3秒的延迟。但是,对于异步调用,程序在“after async”之后无限期挂起。我希望“异步之前”和“异步之后”能够连续快速打印(因为ExecuteAsync只返回一个任务),然后“等待的异步”会在三秒钟后打印(或者更确切地说,至少是三秒钟后;如果ExecuteAsync之
ExecuteAsync
只返回一个任务),然后“等待的异步”会在三秒钟后打印(或者更确切地说,至少是三秒钟后;如果ExecuteAsync
之间有大量逻辑,并且当等待该任务时,它可能会更长)
这使我在进行更改的行上出现了一个隐式转换错误:
无法将类型“System.Threading.Tasks.Task”隐式转换为“System.Threading.Tasks.Task”
因为我不熟悉任务、操作等的工作方式,所以我不太确定问题出在哪里/什么不匹配。您实际上没有运行任何
任务。所以你在等待一些永远不会完成的事情
要按原样修复代码,可以执行以下操作:
private Foo RunExecute(out Task<Foo> task, bool async = false)
{
Foo outputFoo;
if(async)
{
task = Task.Run(() => makeFoo());
outputFoo = null;
}
else
{
task = null;
outputFoo = makeFoo();
}
return outputFoo;
}
为了了解C#的线程构造是如何工作的,我编写了一个简单的测试程序,它执行相同的任务-睡眠3秒钟,然后返回一个对象-同步和异步
您的实际代码相当复杂。让我们从“最简单的”3秒钟同步睡眠开始:
class Program
{
static void Main(string[] args)
{
TestClass tceOn = new TestClass();
Stopwatch s = Stopwatch.StartNew();
s.Checkpoint("Before sync on");
tceOn.Execute();
s.Checkpoint("After sync on");
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
}
class TestClass
{
public Foo Execute()
{
Thread.Sleep(3000);
return new Foo();
}
}
class Foo { }
现在,要创建一个异步等价物,首先从“leaves”(在本例中为TestClass.Execute中的Thread.Sleep
)开始,然后逐步执行。这是将代码转换为异步代码的自然方式(在本例中除外,我们将并排而不是就地创建异步代码)。第一步始终是识别阻塞操作(Thread.Sleep
)并发现异步等价物(在本例中为Task.Delay
):
关于ContinueWith
的最后一个问题,答案是“不要使用ContinueWith
;而是使用wait
。您需要以某种方式运行任务。它不能完全靠自己完成。:)这是wait
中非常常见的问题。我建议您阅读Stephen Cleary在这方面的优秀著作,特别是Async一路,第二段等等,因为它与您的问题直接相关。除了Kirk Woll的评论之外,Stephen还有,后面谈到了死锁是如何发生的(如您所说)如果代码写得不正确,异步操作可能会发生。我知道这是一种糟糕的形式,但为一个微小的更新写一篇新文章似乎是一种浪费。请您再看一看我的更新,好吗?您的ContinueWith()
返回一个Task
,因为lambda有void
返回类型(即不返回任何内容)。您需要一个返回Foo
的任务,即任务
如果要分配给任务
参数,则ContinueWith()
必须返回Foo
。
private Foo RunExecute(out Task<Foo> task, bool async = false)
{
Foo outputFoo;
if(async)
{
task = Task.Run(() => makeFoo());
outputFoo = null;
}
else
{
task = null;
outputFoo = makeFoo();
}
return outputFoo;
}
private Foo RunExecute(out Task<Foo> task, bool async = false)
{
Foo outputFoo;
if(async)
{
task = makeFooAsync();
outputFoo = null;
}
else
{
task = null;
outputFoo = makeFoo();
}
return outputFoo;
}
async Task<Foo> makeFooAsync()
{
await Task.Delay(3000);
return new Foo();
}
private Task<Foo> RunExecute(bool async = false)
{
Foo outputFoo;
if(async)
{
return makeFooAsync();
}
else
{
return Task.FromResult(makeFoo());
}
}
class Program
{
static void Main(string[] args)
{
TestClass tceOn = new TestClass();
Stopwatch s = Stopwatch.StartNew();
s.Checkpoint("Before sync on");
tceOn.Execute();
s.Checkpoint("After sync on");
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
}
class TestClass
{
public Foo Execute()
{
Thread.Sleep(3000);
return new Foo();
}
}
class Foo { }
class TestClass
{
public async Task<Foo> ExecuteAsync()
{
await Task.Delay(3000);
return new Foo();
}
public Foo Execute()
{
Thread.Sleep(3000);
return new Foo();
}
}
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
Console.WriteLine("Press any key to end");
Console.ReadKey();
}
static async Task MainAsync()
{
TestClass tceOn = new TestClass();
Stopwatch s = Stopwatch.StartNew();
s.Checkpoint("Before sync on");
tceOn.Execute();
s.Checkpoint("After sync on");
s.Checkpoint("Before async on");
Task<Foo> fooTask = tceOn.ExecuteAsync();
s.Checkpoint("After async on");
Foo foo = await fooTask;
s.Checkpoint("Awaited async on");
}
}