RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 780270
Accepted
iluxa1810
iluxa1810
Asked:2020-02-05 04:42:03 +0000 UTC2020-02-05 04:42:03 +0000 UTC 2020-02-05 04:42:03 +0000 UTC

TaskCompletionSource<T> 的意义何在?什么时候最好使用它?

  • 772

我有点不明白上课的意思TaskCompletionSource<T>。一些消息来源写道,最好从方法而不是通常的Task.Run().

这有什么意义吗?那是什么,所以我将能够await在呼叫方呼叫。

c#
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    VladD
    2020-02-05T05:52:56Z2020-02-05T05:52:56Z

    TaskCompletionSource- 当您无法Task使用标准工具创建“基础”时,这是非常极端的情况。让我解释一下我的意思。

    如果您正在创建Task,有两种常见的方法可以做到这一点。

    首先,如果您的代码不等待,而是正在积极运行,例如,执行计算(受 CPU 限制),您可以使用Task.Run或其类似物将其发送到线程池。

    其次,如果您使用其他异步操作,您将创建async一个在其中执行await其他异步操作的方法。.NET 提供了许多开箱即用的异步操作,例如 .NETNetworkStream.ReadAsync或Dispatcher.InvokeAsync.

    但是,如果您需要自己创建一个原始异步操作,而不是用其他现成的异步操作来表达怎么办?最里面的Task方法是如何创建的?

    这是您需要它的地方TaskCompletionSource。

    例如,您想异步等待一个事件。为此,您需要将事件转换为Task. 这样做是这样的:我们订阅一个事件,当它到达时,我们完成Task.

    Task<char> WaitInput()
    {
        var tcs = new TaskCompletionSource<char>();
        source.InputReceived += (o, args) => tcs.SetResult(args.Input);
        return tcs.Task;
    }
    

    一个更严格的取消订阅选项,它TaskCompletionSource被用作内部选项Task,以便在结束后有时间取消订阅:

    async Task<char> WaitInput()
    {
        var tcs = new TaskCompletionSource<char>();
        SourceInputHandler handler = (o, args) => tcs.SetResult(args.Input);
        source.InputReceived += handler;
        try
        {
            return await tcs.Task;
        }
        finally
        {
            source.InputReceived -= handler;
        }
    }
    

    真实代码中的另一个示例:启动并等待进程结束:

    Task ExecuteProcess(string path)
    {
        var p = new Process() { EnableRaisingEvents = true, StartInfo = { FileName = path } };
        var tcs = new TaskCompletionSource<bool>();
        p.Exited += (sender, args) => { tcs.SetResult(true); p.Dispose(); };
        // запуск выгружаем на пул потоков, потому что он медленный
        Task.Run(() => p.Start());
        return tcs.Task;
    }
    

    另一个例子取自DispatcherThread. 我们需要等到线程启动并进入“工作”状态。这通常用于 this AutoResetEvent,但在等待时不愿意阻塞,并且使用起来要容易得多TaskCompletionSource:

    static public Task<DispatcherThread> CreateAsync()
    {
        var waitCompletionSource = new TaskCompletionSource<DispatcherThread>();
        var thread = new Thread(() =>
        {
            // тут могут быть любые настройки
            waitCompletionSource.SetResult(new DispatcherThread());
            Dispatcher.Run();
        });
    
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    
        return waitCompletionSource.Task;
    }
    

    Task我们看到,通过这种方式,基本上可以变成任何操作。

    其他相关阅读:TPL 和传统 .NET Framework 异步编程。


    摘要:甚至TaskCompletionSource可以让您变成Task不提供await-able接口的异步操作。

    • 29
  2. v.slobodzian
    2020-02-05T05:25:40Z2020-02-05T05:25:40Z

    有一个关于 SO 的问题。在这里阅读。最佳答案之一的翻译:

    根据我的经验TaskCompletionSource,非常适合将旧的异步模板迁移到现代模板async/await。

    我能想到的最有用的例子是使用Socket. 它具有旧的 APM 和 EAP 模板,但没有awaitable Task具有TcpListener和TcpClient.

    我个人在课堂上有一些问题,我NetworkStream更喜欢raw 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();
    
    • 9

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5