2016-06-06 62 views
2

Taskdoes not maintain a wait handle由于性能方面的原因,只有懒惰地构造一个,如果代码要问一个。任务如何知道它何时完成?

Task怎么知道它已经完成?

有人会争辩说,实施者将在其执行TaskCompletionSource的结果,但可以解释只有现代化的实现,并重新写入如System.IO.FileStream.Begin/EndReadTask

我跟着Task.IsComplete属性;几乎在每种情况下,内部按位标志字段(m_stateFlags)都由TrySetResult/TrySetException方法设置以指示任务的状态。

但这并不包括所有情况。

这样的方法呢?

public async Task FooAsync() 
{ 
    await Task.Run(() => { }); 
} 
+0

你能解释downvote吗? –

+2

我没有找到一个理由为什么有人应该投票的问题或答案,既然都是好的,但由OP本身,所以投了两个 –

+0

@MrinalKamboj非常感谢。 :-) –

回答

0

我在回答我自己的问题,因为我突然想起我知道答案。

当使用C#语言支持功能

它的状态机。

如果异步方法的实施者使用C#语言支持,如方法声明中的async关键字和方法体内部的关键字await等待该任务固有的操作,则在任务的范围内状态机通过设置任务的结果来通知任务完成。

例如,如果他的执行是这样:

// client code 
public async void TopLevelMethod() 
{ 
    await MyMethodAsync(); 
} 

// library code -- his implementation 
public async Task MyMethodAsync() 
{ 
    await AnotherOperationAsync(); 
} 

随后的MyMethodAsync建成后,将委托给编译器生成的状态机。

当然,AnotherOperationAsync的完成信号也将由编译器生成的状态机处理,但这不是重点。

产品召回MoveNext方法中的状态表示任务完成状态和在MoveNext调用延续回调内部的块,这还呼吁AsyncXXXMethodBuilderSetResult

使用C#语言支持功能

然而,如果异步方法的实施者没有利用的C#语言特性,那么它是实施者的责任信号通过在TaskCompletionSource对象上设置相关结果,例外或取消属性来完成任务。

例如,

public Task MyMethodAsync() 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    try 
    { 
    AnotherOperation(); 
    tcs.SetResult(); 
    } 
    catch(Exception ex) 
    { 
    tcs.SetException(ex); 
    } 

    return tcs.Task; 
} 

如果实施者没有使用TPL支持或使用旧.NET API调用另一个操作异步,然后过,它是实施者的责任,通过一个明确的设置任务的状态信号任务完成Try/SetResult/Exception等方法。

例如,

public Task MyMethodAsync() 
{ 
    var tcs = new TaskCompletionSource... 
    var autoReseEvent = ... 
    ThreadPool.QueueUserWorkItem(new WaitCallback(() => 
            { 
            /* Work */ 
            Thread.SpinWait(1000); 
            tcs.SetResult(...); 
            autoResetEvent.Set(); 
            };)...; 

    return tcs.Task; 
} 

一种不明智的案例

等待一个任务,当然,要使用await关键字的最佳方式。但是,如果实施异步API,实施者会这样做:

public Task MyMethodAsync() 
{ 
    return Task.Run(...); 
} 

这会让他的API的消费者感到酸口,我想呢?

Task.Run应该只能用于火灾并忘记您不关心任务完成时间点的情况。

的一个例外是,如果你使用await关键字期待已久的由调用返回的任务Task.Run,如下所示的代码片段,在这种情况下,你会使用的语言支持,在第一部分中描述。

public async Task MyMethodAsync() 
{ 
    await Task.Run(...); 
} 
+0

我不介意downvote和不愿意为downvote提供解释一样。如果我的理解有问题,这只会帮助我知道,而且我很乐意接受批评。如果还有其他原因,比如堆栈溢出策略等,那么它也会帮助我知道,就问题而言,我的思维过程并没有错。你没有提供帮助,因为没有提供一个原因与downvote。 –

+0

(注意:我不是downvoter。)一般来说,有更多的理由不使用'Task.Run()'。主要的原因是调用者不希望工作被卸载,特别是同步工作,即调用者期望真正的异步。最后一个例子中的'async' /'await'是普通的开销,除非'Task.Run'立即抛出一个异常。 – acelent

+0

@acelent我不明白你使用的一些短语。对于例如很多时候,人们会将阻塞I/O绑定的调用称为* asynchronous *甚至* threadless *任务,但在其他时候,有些文章会使用术语* synchronous * work来引用相同的工作。当你说,*“调用者不希望工作被卸载,特别是同步工作”*,你的意思是说,如果执行的工作是阻塞的I/O绑定操作,卸载到线程池线程不会做任何好事?如果这就是你的意思,我同意。 Contd ... –

3

怎么那么任务知道它已经完成?

当我描述我的博客(overviewmore detail),有两种类型的任务:任务委派(其中执行代码),并承诺任务(代表事件)。

委托任务在委托完成时完成。

承诺使用TaskCompletionSource<T>(或BCL内部的等效方法)从外部信号完成任务。

+0

由于我们在谈论内部:似乎有另一种内部机制来完成任务。似乎是延迟和Unwraps等事情的优化。 – usr

相关问题