RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 652137
Accepted
iRumba
iRumba
Asked:2020-04-11 10:59:41 +0000 UTC2020-04-11 10:59:41 +0000 UTC 2020-04-11 10:59:41 +0000 UTC

什么是 Task.Yield()?

  • 772

我不明白它是什么、它是如何工作的以及在什么情况下使用它。有人可以用俄语解释吗?

c#
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    Pavel Mayorov
    2020-04-11T12:52:09Z2020-04-11T12:52:09Z

    此方法返回一个旨在传递给 operator 的特殊值await,并且与此运算符隔离是没有意义的。

    该构造await Task.Yield()做一件相当简单的事情——它中断当前方法并立即在当前同步上下文中安排它的继续。

    这种设计用于不同的目的。

    首先,此构造可用于立即将控制权返回给调用代码。例如,当从事件处理程序调用时,事件将被视为已处理:

    protected override async void OnClosing(CancelEventArgs e)
    {
        e.Cancel = true;
        await Task.Yield(); 
        // (какая-то логика)
    }
    

    其次,此构造用于清理同步调用上下文。例如,这是您可以“关闭”当前事务(环境事务)的方式:

    using (var ts = new TransactionScope()) {
      // ...
      Foo();
      // ...
      ts.Complete();
    }
    
    async void Foo() {
      // ... тут мы находимся в контексте транзакции
      if (Transaction.Current != null) await Task.Yield();
      // ... а тут его уже нет!
    }
    

    第三,这个构造可以清除调用堆栈。如果程序在处理一堆嵌套延续时因堆栈溢出而崩溃,这将很有用。

    例如,考虑一个简化的实现AsyncLock:

    class AsyncLock
    {
        private Task unlockedTask = Task.CompletedTask;
    
        public async Task<Action> Lock()
        {
            var tcs = new TaskCompletionSource<object>();
    
            await Interlocked.Exchange(ref unlockedTask, tcs.Task);
    
            return () => tcs.SetResult(null);
        }
    }
    

    在这里,传入的锁请求在延续时隐式排队。看起来会出什么问题?

    private static async Task Foo()
    {
        var _lock = new AsyncLock();
        var unlock = await _lock.Lock();
    
        for (var i = 0; i < 100000; i++) Bar(_lock);
    
        unlock();
    }
    
    private static async void Bar(AsyncLock _lock)
    {
        var unlock = await _lock.Lock();
        // do something sync
        unlock();
    }
    

    在这里,该方法的延续Bar在另一个方法调用 的同时Bar被调用unlock()。事实证明,方法Bar和委托之间存在间接递归unlock,这会迅速吞噬堆栈并导致其溢出。

    添加一个调用Task.Yield()会将执行转移到一个“干净”的堆栈帧,错误将消失:

    class AsyncLock
    {
        private Task unlockedTask = Task.CompletedTask;
    
        public async Task<Action> Lock()
        {
            var tcs = new TaskCompletionSource<object>();
    
            var prevTask = Interlocked.Exchange(ref unlockedTask, tcs.Task);
    
            if (!prevTask.IsCompleted) 
            {
              await prevTask;
              await Task.Yield();
            }
    
            return () => tcs.SetResult(null);
        }
    }
    

    顺便说一下,修复上面代码的另一种方法是使用标志RunContinuationsAsynchronously:

    class AsyncLock
    {
        private Task unlockedTask = Task.CompletedTask;
    
        public async Task<Action> Lock()
        {
            var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
    
            await Interlocked.Exchange(ref unlockedTask, tcs.Task);
    
            return () => tcs.SetResult(null);
        }
    }
    

    第四,当在 UI 线程上使用时,这种设计允许您处理累积的 I/O 事件,这对于长期的 UI 更新很有用。

    例如,当向表中添加一百万行时,程序将在添加所有行之前不响应用户操作。但是,如果,例如,在每千行添加之后,await Task.Yield()插入一个调用,程序将能够处理用户操作,并且看起来不会被冻结。

    在WinForms中,一个方法可以用于相同的目的Application.DoEvents()- 但它的过度使用会导致堆栈溢出。await Task.Yield()是可以在WinForms和WPF中使用的通用方式。

    • 45
  2. Maxim T
    2020-11-09T16:26:54Z2020-11-09T16:26:54Z

    我认为这里没有人回答为什么需要 Task.Yield 的问题。当一个任务(Task)使用无限循环(一般来说,任何长时间的同步工作)并且可以从池中保留一个线程只为它自己使用并防止其他任务使用这个线程时需要它。Task.Yield 将任务重新提交到线程池队列中,其他一直在等待执行的任务就可以使用这个挂起的线程。

    例子:

      CancellationTokenSource cts;
      void Start()
      {
            cts = new CancellationTokenSource();
            // Запускаем асинхронную операцию
            var task = Task.Run(() => SomeWork(cts.Token), cts.Token);
            //  Ждем окончания
            // После окончания операции обрабатываем результат/отмену/исключения
        }
    
        async Task<int> SomeWork(CancellationToken cancellationToken)
        {
            int result = 0;
    
            bool loopAgain = true;
            while (loopAgain)
            {
                // Что-то делаем ...
    
                loopAgain = /* проверка на окончание цикла && */  cancellationToken.IsCancellationRequested;
                if (loopAgain) {
                    // переотправляет задачу в очередь пула потоков чтобы другие задачи, которые ожидали выполнения смогли использовать данный поток
                    await Task.Yield();
                }
            }
            cancellationToken.ThrowIfCancellationRequested();
            return result;
        }
    
        void Cancel()
        {
            // Запрашиваем отмену операции
            cts.Cancel();
        }
    
    • 7

相关问题

Sidebar

Stats

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

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +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
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +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