AFAIK,它所知道的只是在某些时候,它的SetResult或SetException方法被调用来完成通过Task属性暴露的Task<T>。

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

我在这里看到了一个例子:

如果我需要一种方法来异步执行Func<T>,并有一个任务<T> 来表示这个操作。

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 ?


当前回答

在这篇来自“。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();
    });
}

其他回答

TaskCompletionSource is used to create Task objects that don't execute code. In real world scenarios, TaskCompletionSource is ideal for I/O bound operations. This way, you get all the benefits of tasks (e.g. return values, continuations, etc) without blocking a thread for the duration of the operation. If your "function" is an I/O bound operation, it isn't recommended to block a thread using a new Task. Instead, using TaskCompletionSource, you can create a slave task to just indicate when your I/O bound operation finishes or faults.

在这篇博文中,Levi Botelho描述了如何使用TaskCompletionSource为Process编写异步包装器,这样您就可以启动它并等待它的终止。

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

Blazor的WebAssemblyHost也使用这个来防止. net虚拟机停止。

await new TaskCompletionSource().Task;

这可能是过于简化的事情,但TaskCompletion源代码允许等待一个事件。从tc开始。SetResult只在事件发生时设置,调用者可以等待任务。

观看这段视频了解更多:

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

似乎没有人提到,但我想单元测试也可以被认为是真实的生活。

我发现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的这种用法似乎是“不执行代码的任务对象”的另一种情况。