关于Task.Yield()

文章介绍了awaitTask.Yield在异步方法中的作用,即暂停同步过程并让主线程执行其他任务。实验显示,使用awaitTask.Yield可以显著缩短主线程的等待时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

await Task.Yield()
这个表达式是用于在异步方法内中断同步过程,把线程优先权交还给其它过程,相当于await Task.Run(()=>{})。
当同步过程执行到await时,就会停止这个过程,把优先权交还给调用异步方法的主过程。有时候,异步方法中的同步过程需要消耗很长的时间,使用这个表达式可以把优先权交还给主过程。

internal class Program
{
    static void Main(string[] args)
    {
        
        A a = new A();
        Task t = a.Dosome();
        Console.WriteLine("开始主过程中的循环,当前时间为{0}", a.sw.Elapsed.TotalMilliseconds);
        for (int i = 0; i < 100000000; i++)
        {
        }
        Console.WriteLine("结束主过程中的循环,当前时间为{0}", a.sw.Elapsed.TotalMilliseconds);
        Console.ReadLine();
    }
}

  class A
{
    public Stopwatch sw = Stopwatch.StartNew();
    public async Task Dosome()
    {
        //await Task.Yield();
        //await Task.Run(()=> { }); 
        Console.WriteLine("开始异步方法中的循环,时间为{0}", sw.Elapsed.TotalMilliseconds);
        for(int i = 0;i<100000000;i++)
        {
        }
        Console.WriteLine("结束异步方法中的循环,时间为{0}", sw.Elapsed.TotalMilliseconds);
    }    
}

当前程序运行的结果是:

开始异步方法中的循环,时间为0.4532
结束异步方法中的循环,时间为284.8642
开始主过程中的循环,当前时间为285.0233
结束主过程中的循环,当前时间为490.9204

去掉任一注释符号后:

开始主过程中的循环,当前时间为5.1991
开始异步方法中的循环,时间为5.4125
结束主过程中的循环,当前时间为224.6115
结束异步方法中的循环,时间为310.4565
### 任务调度中的 `Task.Yield` 在异步编程中,`Task.Yield()` 是一个非常有用的工具,它主要用于将当前的执行上下文释放回线程池,从而允许其他任务在当前任务继续执行之前运行。这种机制在 .NET 中特别重要,因为它可以帮助避免线程池饥饿问题,并确保更高效的资源利用。 #### 主要用途 1. **避免线程池饥饿**:当一个长时间运行的任务占用了一个线程池线程时,可能会导致其他任务无法及时获得线程执行。通过调用 `Task.Yield()`,可以显式地让出当前线程,使得其他排队等待的任务有机会被执行[^1]。 2. **改变执行上下文**:`Task.Yield()` 可以用来强制后续代码在一个新的线程上执行,而不是继续在原来的线程上执行。这对于需要切换到不同的上下文(例如 UI 线程)的情况特别有用[^3]。 3. **测试和调试**:在编写单元测试或进行并发行为分析时,使用 `Task.Yield()` 可以更容易地模拟多线程环境下的复杂情况,帮助开发者发现潜在的问题[^2]。 #### 示例代码 下面是一些具体的示例来展示 `Task.Yield()` 的应用: ##### 示例 1: 避免线程池饥饿 ```csharp static async Task Process() { await Task.Yield(); // 让出当前线程给其他任务 var tcs = new TaskCompletionSource<bool>(); Task.Run(() => { Thread.Sleep(1000); tcs.SetResult(true); }); await tcs.Task; // 等待结果 } ``` 在这个例子中,`Process` 方法首先调用 `await Task.Yield()` 来释放当前线程,这样可以让其他排队的任务先执行。然后启动一个新的任务来完成耗时操作,并通过 `TaskCompletionSource` 来通知结果。 ##### 示例 2: 改变执行上下文 ```csharp static async Task RunAsync() { Console.WriteLine("RunAsync方法前" + System.Threading.Thread.CurrentThread.ManagedThreadId); Task<Task> task = null; task = GetOwnTaskAsync(); var foundTask = await task; Console.WriteLine("RunAsync方法后" + System.Threading.Thread.CurrentThread.ManagedThreadId); async Task<Task> GetOwnTaskAsync() { Console.WriteLine("前" + System.Threading.Thread.CurrentThread.ManagedThreadId); await Task.Yield(); // 强制后续代码在新线程上执行 Console.WriteLine("后" + System.Threading.Thread.CurrentThread.ManagedThreadId); return task; } } ``` 此示例展示了如何通过 `Task.Yield()` 来切换执行上下文。注释掉 `await Task.Yield()` 后,可以看到输出的线程 ID 在前后保持一致;而启用后,则会看到不同的线程 ID,表明代码确实在新的线程上执行了。 ##### 示例 3: 测试和调试 ```csharp static void Main(string[] args) { Task.Run(() => RunAsync()).Wait(); Console.ReadLine(); } static async Task RunAsync() { Task<Task> task = null; task = GetOwnTaskAsync(); var foundTask = await task; Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}"); async Task<Task> GetOwnTaskAsync() { await Task.Yield(); // 模拟异步延迟 return task; } } ``` 在这个示例中,`GetOwnTaskAsync` 使用 `Task.Yield()` 来模拟异步延迟,这有助于在测试环境中观察不同时间点的行为。 #### 总结 `Task.Yield()` 提供了一种简单但有效的方式来控制异步任务的执行流程,特别是在处理线程池管理和上下文切换时非常有用。合理使用 `Task.Yield()` 可以提高应用程序的性能和响应性,同时也有助于调试和测试复杂的并发场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值