其中是Task对象的返回状态
本文关键字:返回 状态 对象 Task | 更新日期: 2023-09-27 18:08:12
以下代码编译并运行良好。但是Consumer((和Producer((方法的return语句在哪里?
class Program
{
static BufferBlock<Int32> m_buffer = new BufferBlock<int>(
new DataflowBlockOptions { BoundedCapacity = 10 });
public static async Task Producer() <----- How is a Task object returned?
{
while (true)
{
await m_buffer.SendAsync<Int32>(DateTime.Now.Second);
Thread.Sleep(1000);
}
}
public static async Task Consumer() <----- How is a Task object returned?
{
while (true)
{
Int32 n = await m_buffer.ReceiveAsync<Int32>();
Console.WriteLine(n);
}
}
static void Main(string[] args)
{
Task.WaitAll(Consumer(), Producer());
}
}
虽然您的问题陈述了显而易见的内容(代码编译(,而其他答案则试图通过示例进行解释,但我认为答案最好在以下两篇文章中描述:
- "高于表面"的答案-全文如下:http://msdn.microsoft.com/en-us/magazine/hh456401.aspx
[…]C#和Visual Basic[…]为编译器提供了足够的提示在幕后为您建立必要的机制。这个解决方案有两部分:一部分在类型系统中,另一部分在语言
CLR 4版本定义了类型Task[…]来表示"一些将产生T型结果的工作未来。"工作将在未来完成,但不返回结果"由非泛型Task类型表示。
确切地说,T类型的结果将如何在未来是特定任务的实现细节;[…]
解决方案的语言部分是新的await关键字。一位常客方法调用的意思是"记住你在做什么,运行这个方法直到它完全完成了,然后从你停止的地方重新开始,现在知道该方法的结果。"相比之下,等待的表情,意思是"评估该表达式以获得表示作品的对象"这将在未来产生结果。注册的剩余部分当前方法作为与的延续关联的回调这项任务。一旦任务被产生并且回调被注册,立即将控制权交给我的来电者。">
2.发动机罩下的说明如下:http://msdn.microsoft.com/en-us/magazine/hh456403.aspx
〔…〕Visual Basic和C#〔…〕让你表达不连续顺序代码。[…]当Visual Basic或C#编译器挂起时对于异步方法,它在编译:不直接支持该方法的不连续性由底层运行时执行,并且必须由编译器模拟。所以编译器不必将方法拆分为多个位为你做这件事。[…]
编译器将异步方法转换为状态机。这个状态机跟踪您在执行中的位置和内容您所在的州是。〔…〕
异步方法产生任务。更具体地说,异步方法返回Task或Task类型之一的实例System.Threading.Tasks,并且该实例是自动生成的。它不一定(也不可能(由用户代码提供。[…]
从编译器的角度来看,生成任务是容易的部分。它依赖于框架提供的任务构建器概念,可以在System.Runtime.CompilerServices[…]生成器允许编译器获取一个Task,然后让它完成该Task并返回一个结果或例外[…]任务生成器是专门用于编译器消耗。[…]
[…]围绕生产和消费建立一个状态机的任务。从本质上讲,来自原始方法的所有用户逻辑被放入恢复委托中,但本地人的声明被取出,这样它们就可以在多次调用中幸存下来。此外引入了一个状态变量来跟踪事情发展到什么程度,并且恢复委托中的用户逻辑被包裹在一个大开关,该开关查看状态并跳到相应的标签。所以每当调用恢复时,它都会跳回到它所在的位置上次中断。
当方法没有返回语句时,其返回类型为Task。查看Async Await 的MSDN页面
异步方法有三种可能的返回类型:
Task<TResult>, Task, void
如果需要Task,则必须指定return语句。摘自MSDN页面:
// TASK<T> EXAMPLE
async Task<int> TaskOfT_MethodAsync()
{
// The body of the method is expected to contain an awaited asynchronous
// call.
// Task.FromResult is a placeholder for actual work that returns a string.
var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString());
// The method then can process the result in some way.
int leisureHours;
if (today.First() == 'S')
leisureHours = 16;
else
leisureHours = 5;
// Because the return statement specifies an operand of type int, the
// method must have a return type of Task<int>.
return leisureHours;
}
async
关键字种类告诉编译器应该将方法主体用作Task主体。简单地说,我们可以说这些都相当于您的例子:
public static Task Producer() <----- How is a Task object returned?
{
return Task.Run(() =>
{
while (true)
{
m_buffer.SendAsync<Int32>(DateTime.Now.Second).Wait();
Thread.Sleep(1000);
}
});
}
public static Task Consumer() <----- How is a Task object returned?
{
return Task.Run(() =>
{
while (true)
{
Int32 n = m_buffer.ReceiveAsync<Int32>().Wait();
Console.WriteLine(n);
}
});
}
当然,你的方法的编译结果将与我的例子完全不同,因为编译器足够聪明,它可以用这种方式生成代码,所以你的方法中的一些行将在上下文(线程(上调用,上下文调用该方法,其中一些行在后台上下文上调用。这就是为什么不建议在async
方法的主体中使用Thread.Sleep(1000);
的原因,因为可以在调用此方法的线程上调用此代码。任务具有等价的Thread.Sleep(1000);
和等价的await Task.Delay(1000)
,后者将在后台线程上调用。正如您所看到的,await
关键字保证了此调用将在后台上下文中调用,并且不会阻塞调用方上下文。
让我们再看一个例子:
async Task Test1()
{
int a = 0; // Will be called on the called thread.
int b = await this.GetValueAsync(); // Will be called on background thread
int c = GetC(); // Method execution will come back to the called thread again on this line.
int d = await this.GetValueAsync(); // Going again to background thread
}
所以我们可以说这将生成代码:
Task Test1()
{
int a = 0; // Will be called on the called thread.
vat syncContext = Task.GetCurrentSynchronizationContext(); // This will help us go back to called thread
return Task.Run(() =>
{
// We already on background task, so we safe here to wait tasks
var bTask = this.GetValueAsync();
bTask.Wait();
int b = bTask.Result;
// syncContext helps us to invoke something on main thread
// because 'int c = 1;' probably was expected to be called on
// the caller thread
var cTask = Task.Run(() => return GetC(), syncContext);
cTask.Wait();
int c = cTask.Result;
// This one was with 'await' - calling without syncContext,
// not on the thread, which calls our method.
var dTask = this.GetValueAsync();
dTask.Wait();
int d = dTask.Result;
});
}
同样,这与您将从编译器中获得的代码不同,但它应该会让您了解它是如何工作的。如果你真的想看看生成的库中会有什么,可以使用例如IlSpy来看看生成的代码。
此外,我真的建议阅读这篇文章异步编程中的最佳实践
这正是async
关键字的含义。它接受一个方法并(通常(将其转换为Task
返回方法。如果正常方法的返回类型为void
,则async
方法将具有Task
。如果返回类型是其他T
,则新的返回类型将是Task<T>
。
例如,要同步下载一个进程的一些数据,您可以编写以下内容:
Foo GetFoo()
{
string s = DownloadFoo();
Foo foo = ParseFoo(s);
return foo;
}
异步版本看起来是这样的:
Task<Foo> GetFoo()
{
string s = await DownloadFoo();
Foo foo = ParseFoo(s);
return foo;
}
请注意,返回类型从Foo
更改为Task<Foo>
,但您仍然只返回Foo
。与void
返回方法类似:因为它们不必包含return
语句,所以async Task
(没有任何<T>
(方法也不必包含。
Task
对象是由编译器生成的代码构建的,所以您不必这样做。