当应TaskCompletionSource 使用?(When should TaskComp

2019-07-21 06:09发布

AFAIK,所有它知道的是,在一些点,其SetResultSetException方法被调用来完成Task<T>通过其暴露的Task属性。

换句话说,它作为生产者对一个Task<TResult>和它的完成。

我看到这里的例子:

如果我需要一种方法来异步执行函数求,并有一个任务来表示操作。

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

Answer 1:

我主要使用它时,只有基于事件的API是可用的( 例如Windows Phone的8座 ):

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关键字。



Answer 2:

在我的经验, TaskCompletionSource是伟大的包装旧异步模式,以现代async/await模式。

最有利的例子,我能想到的是工作时Socket 。 它有老APM和EAP模式,但不是awaitable Task中的方法TcpListenerTcpClient都有。

我个人有几个问题NetworkStream类,喜欢的原始Socket 。 是,我也很喜欢async/await模式,我做了一个扩展类SocketExtender它创建了几个扩展方法Socket

所有这些方法都利用TaskCompletionSource<T>包裹异步调用像这样:

    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方法,使我获得稍许的性能提升出来的没有扯起本地参数的编译器。

那么这一切的美丽:

 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();


Answer 3:

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

这方面的一个很好的例子是,当你使用一个缓存。 你可以有一个GetResourceAsync方法,它看起来在缓存中所请求的资源,并立即返回(不使用一个新的线程,通过使用TaskCompletionSource )如果资源被发现。 只有没有被发现的资源,我们想用一个新的线程,并使用检索Task.Run()

一个代码示例可以看这里: 如何有条件地运行代码asynchonously使用任务



Answer 4:

在这篇博客文章 ,列维·博特略介绍了如何使用TaskCompletionSource写一个过程,这样你可以启动它,并等待其终止的异步包装。

public static Task RunProcessAsync(string processPath)
{
    var tcs = new TaskCompletionSource<object>();
    var process = new Process
    {
        EnableRaisingEvents = true,
        StartInfo = new ProcessStartInfo(processPath)
        {
            RedirectStandardError = true,
            UseShellExecute = false
        }
    };
    process.Exited += (sender, args) =>
    {
        if (process.ExitCode != 0)
        {
            var errorMessage = process.StandardError.ReadToEnd();
            tcs.SetException(new InvalidOperationException("The process did not exit correctly. " +
                "The corresponding error message was: " + errorMessage));
        }
        else
        {
            tcs.SetResult(null);
        }
        process.Dispose();
    };
    process.Start();
    return tcs.Task;
}

及其用法

await RunProcessAsync("myexecutable.exe");


Answer 5:

TaskCompletionSource用于创建不执行代码任务的对象。 在实际场景TaskCompletionSource是理想的I / O密集型操作。 这样,你得到所有的任务(如返回值,延续等)的好处,而不阻塞线程操作的持续时间。 如果你的“功能”是一个IO约束操作不建议阻止使用新任务的线程。 而不是使用TaskCompletionSource你可以创建一个从机任务,只是表示当你的I / O密集型操作完成或故障。



Answer 6:

它看起来像没有提到任何一个,但我想单元测试也可以被认为是真实的生活就够了。

我觉得TaskCompletionSource以嘲讽与异步方法的依赖时是有用的。

在测试实际的程序:

public interface IEntityFacade
{
  Task<Entity> GetByIdAsync(string id);
}

在单元测试:

// set up mock dependency (here with NSubstitute)

TaskCompletionSource<Entity> queryTaskDriver = new TaskCompletionSource<Entity>();

IEntityFacade entityFacade = Substitute.For<IEntityFacade>();

entityFacade.GetByIdAsync(Arg.Any<string>()).Returns(queryTaskDriver.Task);

// later on, in the "Act" phase

private void When_Task_Completes_Successfully()
{
  queryTaskDriver.SetResult(someExpectedEntity);
  // ...
}

private void When_Task_Gives_Error()
{
  queryTaskDriver.SetException(someExpectedException);
  // ...
}

毕竟,TaskCompletionSource的这种用法似乎“不执行代码任务对象”的另一种情况。



Answer 7:

有与本像样的解释现实世界的例子从博客“与.NET并行编程”的帖子 。 你真的应该读它,但这里有一个总结呢。

博客文章显示了两种实现方式为:

延迟“用于创建一个工厂方法‘’任务,那些实际上不会被调度,直到发生了某些用户提供超时。”

所示的第一实施基于Task<>和具有两个主要缺陷。 第二实施后接着通过使用以减轻这些TaskCompletionSource<>

下面是第二次执行:

public static Task StartNewDelayed(int millisecondsDelay, Action action)
{
    // Validate arguments
    if (millisecondsDelay < 0)
        throw new ArgumentOutOfRangeException("millisecondsDelay");
    if (action == null) throw new ArgumentNullException("action");

    // Create a trigger used to start the task
    var tcs = new TaskCompletionSource<object>();

    // Start a timer that will trigger it
    var timer = new Timer(
        _ => tcs.SetResult(null), null, millisecondsDelay, Timeout.Infinite);

    // Create and return a task that will be scheduled when the trigger fires.
    return tcs.Task.ContinueWith(_ =>
    {
        timer.Dispose();
        action();
    });
}


Answer 8:

这里我用我真实的场景TaskCompletionSource是实现下载队列时。 在我的情况下,如果用户开始下载100我不想解雇他们全部关闭一次等,而不是返回strated任务的我回到附加到任务TaskCompletionSource 。 一旦下载完成后得到该工作队列线程完成任务。

这里的关键概念是,我去耦当客户端请求一个任务从当它实际上被开始启动。 在这种情况下,因为我不希望客户端必须处理资源管理。

请注意,您可以根据您使用的是C#编译器5(VS 2012+),只要使用异步/地等待着.NET 4看到此处了解更多详情。



Answer 9:

这可能是过于简单化的东西,但TaskCompletion源允许一个等待一个事件。 因为一旦事件发生tcs.SetResult只能设置,调用者可以等待的任务。

观看此视频中更多的内幕:

http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Lucian03-TipsForAsyncThreadsAndDatabinding



Answer 10:

我用TaskCompletionSource直到它被取消运行任务。 在这种情况下,它是一个ServiceBus用户,我通常要为只要在应用程序运行运行。

public async Task RunUntilCancellation(
    CancellationToken cancellationToken,
    Func<Task> onCancel)
{
    var doneReceiving = new TaskCompletionSource<bool>();

    cancellationToken.Register(
        async () =>
        {
            await onCancel();
            doneReceiving.SetResult(true); // Signal to quit message listener
        });

    await doneReceiving.Task.ConfigureAwait(false); // Listen until quit signal is received.
}


文章来源: When should TaskCompletionSource be used?