首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >什么时候应该使用TaskCompletionSource<T>?

什么时候应该使用TaskCompletionSource<T>?
EN

Stack Overflow用户
提问于 2013-03-10 06:24:41
回答 10查看 103.7K关注 0票数 227

它所知道的就是,在某个时刻,它的SetResultSetException方法被调用,以完成通过其Task属性公开的Task<T>

换句话说,它充当Task<TResult>及其完成的生产者。

我在示例中看到了:

如果我需要一种异步执行Func<T>的方法,并且需要一个Task<T>来表示该操作,则使用

代码语言:javascript
复制
public static Task<T> RunAsync<T>(Func<T> function) 
{ 
    if (function == null) throw new ArgumentNullException(“function”); 
    var tcs = new TaskCompletionSource<T>(); 
    ThreadPool.QueueUserWorkItem(_ => 
    { 
        try 
        {  
            T result = function(); 
            tcs.SetResult(result);  
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
    }); 
    return tcs.Task; 
}

如果我没有Task.Factory.StartNew --但是我有Task.Factory.StartNew --可以使用它。

问题:

有没有人可以举例说明一个场景,这个场景与直接TaskCompletionSource有关,而不是与我没有Task.Factory.StartNew的假设情况相关

EN

回答 10

Stack Overflow用户

回答已采纳

发布于 2013-03-10 06:31:23

我主要在只有基于事件的API可用时使用它(for example Windows Phone 8 sockets):

代码语言:javascript
复制
public Task<Args> SomeApiWrapper()
{
    TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>(); 

    var obj = new SomeApi();

    // will get raised, when the work is done
    obj.Done += (args) => 
    {
        // this will notify the caller 
        // of the SomeApiWrapper that 
        // the task just completed
        tcs.SetResult(args);
    }

    // start the work
    obj.Do();

    return tcs.Task;
}

因此,当它与C#5 async关键字一起使用时特别有用。

票数 260
EN

Stack Overflow用户

发布于 2014-02-02 02:29:00

根据我的经验,TaskCompletionSource非常适合将旧的异步模式包装成现代的async/await模式。

我能想到的最有益的例子是在使用Socket时。它具有旧的APM和EAP模式,但没有TcpListenerTcpClient所具有的awaitable Task方法。

我个人对NetworkStream类有几个问题,我更喜欢原始的Socket。因为我也喜欢async/await模式,所以我创建了一个扩展类SocketExtender,它为Socket创建了几个扩展方法。

所有这些方法都使用TaskCompletionSource<T>来包装异步调用,如下所示:

代码语言:javascript
复制
    public static Task<Socket> AcceptAsync(this Socket socket)
    {
        if (socket == null)
            throw new ArgumentNullException("socket");

        var tcs = new TaskCompletionSource<Socket>();

        socket.BeginAccept(asyncResult =>
        {
            try
            {
                var s = asyncResult.AsyncState as Socket;
                var client = s.EndAccept(asyncResult);

                tcs.SetResult(client);
            }
            catch (Exception ex)
            {
                tcs.SetException(ex);
            }

        }, socket);

        return tcs.Task;
    }

我将socket传递给BeginAccept方法,这样我就可以从编译器中获得轻微的性能提升,而不必提升本地参数。

这一切的美妙之处在于:

代码语言:javascript
复制
 var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
 listener.Listen(10);

 var client = await listener.AcceptAsync();
票数 83
EN

Stack Overflow用户

发布于 2013-03-10 15:16:20

对我来说,使用TaskCompletionSource的一个经典场景是,我的方法可能不一定要执行耗时的操作。它允许我们选择我们想要使用新线程的特定情况。

一个很好的例子是当你使用缓存的时候。您可以使用GetResourceAsync方法,该方法在缓存中查找请求的资源,如果找到资源,则立即返回(不使用新线程,使用TaskCompletionSource)。只有在没有找到资源的情况下,我们才会使用一个新线程,并使用Task.Run()检索它。

代码示例可以在这里看到:How to conditionally run a code asynchonously using tasks

票数 37
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15316613

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档