C# TaskCompletionSource<T>使用

什么时候使用?

最典型的使用场景就是包装之前的异步方案(如Event),使方法可以通过async/await去调用。

示例

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#异步方式启动进程

using System;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;

// based on https://gist.github.com/AlexMAS/276eed492bc989e13dcce7c78b9e179d
public static class ProcessAsyncHelper
{
    public static async Task<ProcessResult> RunProcessAsync(string command, string arguments, int timeout)
    {
        var result = new ProcessResult();

        using (var process = new Process())
        {
            process.StartInfo.FileName = command;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.UseShellExecute = false;
            //process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;

            var outputBuilder = new StringBuilder();
            var outputCloseEvent = new TaskCompletionSource<bool>();

            process.OutputDataReceived += (s, e) =>
            {
                if (e.Data == null)
                {
                    outputCloseEvent.SetResult(true);
                }
                else
                {
                    outputBuilder.Append(e.Data);
                }
            };

            var errorBuilder = new StringBuilder();
            var errorCloseEvent = new TaskCompletionSource<bool>();

            process.ErrorDataReceived += (s, e) =>
            {
                if (e.Data == null)
                {
                    errorCloseEvent.SetResult(true);
                }
                else
                {
                    errorBuilder.Append(e.Data);
                }
            };

            var isStarted = process.Start();
            if (!isStarted)
            {
                result.ExitCode = process.ExitCode;
                return result;
            }

            // Reads the output stream first and then waits because deadlocks are possible
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            // Creates task to wait for process exit using timeout
            var waitForExit = WaitForExitAsync(process, timeout);

            // Create task to wait for process exit and closing all output streams
            var processTask = Task.WhenAll(waitForExit, outputCloseEvent.Task, errorCloseEvent.Task);

            // Waits process completion and then checks it was not completed by timeout
            if (await Task.WhenAny(Task.Delay(timeout), processTask) == processTask && waitForExit.Result)
            {
                result.ExitCode = process.ExitCode;
                result.Output = outputBuilder.ToString();
                result.Error = errorBuilder.ToString();
            }
            else
            {
                try
                {
                    // Kill hung process
                    process.Kill();
                }
                catch
                {
                    // ignored
                }
            }
        }

        return result;
    }


    private static Task<bool> WaitForExitAsync(Process process, int timeout)
    {
        return Task.Run(() => process.WaitForExit(timeout));
    }


    public struct ProcessResult
    {
        public int? ExitCode;
        public string Output;
        public string Error;
    }
}

Ref

https://stackoverflow.com/questions/8575889/how-to-conditionally-run-a-code-asynchonously-using-tasks
https://gist.githubusercontent.com/georg-jung/3a8703946075d56423e418ea76212745/raw/1cc87c475a4949ba7a8e5481120e291d8061acdb/ProcessAsyncHelper.cs

posted @ 2022-11-09 13:44  talentzemin  阅读(413)  评论(0编辑  收藏  举报