在 Task.WhenAll 中执行多个任务时的 C# 线程

2024-02-21

如果在单个线程上执行以下操作,会发生什么:

await Task.WhenAll(items.select(x => SomeAsyncMethod(x)))

// Where SomeAsyncMethod is defined like this (writeAsync is pure async io)
async Task SomeAsyncMethod(Item item){
  await myDevice.writeAsync(...).ConfigureAwait(false);
  //do some cpu intensive stuff...
}

并说有 10.000 个项目items。当每个SomeAsyncMethod在等待之后继续,然后在线程池中的线程上执行此操作。所以当许多SomeAsyncsMethods 返回将同时获取线程池中的多个线程,或者仅单个线程执行“执行一些 CPU 密集型操作”SomeAsyncMethod在这种情况下的任何特定时刻?

更新:好的,这是一个示例程序。当我在具有 8 个逻辑核心的 PC 上对此进行测试时,最小线程为 12 或 13,最大线程在 35-40 范围内结束。 因此看起来好像将在逻辑核心上创建最多 4 个线程。创建 10.000 个或 100.000 个文件并不重要 - 使用相同的最大线程数 - 也许这是因为所有任务都排队等待访问文件系统?请注意,该程序将在 c:\tmp\asynctest 中创建大量小文件:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication4 {
    internal class Program {
        private static void Main(string[] args) {
            var myDevice = new MyDevice();
            var ints = new List<int>();
            for (var i = 0; i < 10000; i++) {
                ints.Add(i);
            }
            var task = Task.WhenAll(ints.Select(i => myDevice.WriteTextAsync(i.ToString())));
            task.Wait();
            Console.WriteLine("Max thread count = " + myDevice.MaxThreadCount);
            Console.WriteLine("Min thread count = " + myDevice.MinThreadCount);
            Console.ReadLine();
        }
    }

    public class MyDevice {
        public ConcurrentDictionary<string, string> ThreadIds;
        public int MaxThreadCount;
        public int MinThreadCount = Process.GetCurrentProcess().Threads.Count;
        public async Task WriteTextAsync(string text) {
            var filePath = @"c:\tmp\asynctest\" + text + ".txt";
            var encodedText = Encoding.Unicode.GetBytes(text);
            using (var sourceStream = new FileStream(filePath,
                FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) {
                await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).ConfigureAwait(false);
                MaxThreadCount = Math.Max(MaxThreadCount, Process.GetCurrentProcess().Threads.Count);
                MinThreadCount = Math.Min(MinThreadCount, Process.GetCurrentProcess().Threads.Count);
            }
        }
    }
}

更新 2。现在,如果我启动多个线程,每个线程同时执行大量 aysnc io 任务,那么与更新 1 中的单线程示例相比,看起来总共并没有使用更多线程。在我刚刚运行的测试中,其中每个文件由 4 个线程创建 10.000 个文件,然后最大线程为 41,最小线程为 12 - 因此似乎有一些集中控制用于异步任务延续的线程数量。下面是一个示例,其中 4 个线程各自启动 10.000 个异步操作:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication4 {
    internal class Program {
        private static void Main(string[] args) {
            var myDevice = new MyDevice();
            var ints = new List<int>();
            const int limit = 10000;
            for (var i = 0; i < limit; i++) {
                ints.Add(i);
            }

            List<Task> jobs = new List<Task>();
            for (var j = 0; j < 4*limit; j+=limit) {
                var jobid = j;
                jobs.Add(Task.Run(() => Runjob(ints, myDevice, jobid)));
            }
            Task.WaitAll(jobs.ToArray());

            Console.WriteLine("Max thread count = " + myDevice.MaxThreadCount);
            Console.WriteLine("Min thread count = " + myDevice.MinThreadCount);
            Console.ReadLine();
        }

        private static void Runjob(List<int> ints, MyDevice myDevice, int jobid) {
            Console.WriteLine("Starting job " + jobid);
            var task = Task.WhenAll(ints.Select(i => myDevice.WriteTextAsync((jobid+i).ToString())));
            task.Wait();
            Console.WriteLine("Finished job " + jobid);
        }
    }

    public class MyDevice {
        public int MaxThreadCount;
        public int MinThreadCount = Process.GetCurrentProcess().Threads.Count;
        public async Task WriteTextAsync(string text) {
            var filePath = @"c:\tmp\asynctest\" + text + ".txt";
            var encodedText = Encoding.Unicode.GetBytes(text);
            using (var sourceStream = new FileStream(filePath,
                FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) {
                await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).ConfigureAwait(false);
                MaxThreadCount = Math.Max(MaxThreadCount, Process.GetCurrentProcess().Threads.Count);
                MinThreadCount = Math.Min(MinThreadCount, Process.GetCurrentProcess().Threads.Count);
            }
        }
    }
}

最有可能的情况是,“CPU 密集型工作”将分别发生在随机线程池线程上 - 如果它确实受 CPU 限制,则每个逻辑核心将获得大约 1-2 个线程来完成工作。

关键点是,虽然继续原来的任务(Task.WhenAll)将在 UI 线程上运行(如果有is当然,同步上下文),各个 I/O 操作的延续将发布到线程池上,因为您明确请求忽略同步上下文(ConfigureAwait(false)).

但是,如果 I/O 请求同步完成,则所有内容也有可能在原始线程上运行。在这种情况下,不会进行异步调度,任务也没有机会切换线程。如果需要确保并行化,则必须使用Task.Run明确地。

还应该注意的是,这主要取决于实现,而不是您可以依赖的东西。对于严重异步 I/O 应用程序来说,这也可能是一个糟糕的方法,因为您可能在 I/O 线程池中的线程上运行 CPU 密集型内容 - 扰乱线程池中线程的框架平衡,并防止新的异步响应出现,直到您完成工作。如果您正在做的工作尤其如此isn't纯粹的 CPU 工作——例如,在 Web 服务器之类的东西上,线程池线程上的阻塞可能会非常痛苦。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Task.WhenAll 中执行多个任务时的 C# 线程 的相关文章

随机推荐

  • C *[] 和 ** 之间的区别

    这可能是一个有点基本的问题 但是写 char 和 char 有什么区别 例如 在 main 中 我可以有一个 char argv 或者我可以使用 char argv 我认为这两种符号之间一定存在某种差异 在这种情况下 根本没有区别 如果您尝
  • 如何在 Java 中向字符串添加换行符?

    在 Java 应用程序中 我创建一个如下所示的字符串 通过串联 String notaCorrente dataOdierna testoNotaCorrente 我的问题是我想在此字符串末尾添加类似 HTML 换行符的内容 将显示在 HT
  • 子串上的熔化和合并 - Python 和 Pandas

    我有数据 其中有类似的数据 id name model ms bp1 cd1 sf1 sa1 rq1 bp2 cd2 sf2 sa2 rq2 1 John 23984 1 23 234 124 25 252 252 62 194 234 2
  • 将参数传递给 AddHostedService

    我正在编写一个 Net Core Windows 服务 下面是一段代码 internal static class Program public static async Task Main string args var isServic
  • 如何使用 Swagger UI 访问 AWS API Gateway 文档

    我已经使用 AWS Api Gateway 创建了 API 然后我记录了所有实体的文档部分 如 API 资源 方法 模型等 然后使用 AWS Gateway Console 我已将文档发布到dev阶段与版本1 但我不确定我 或 API 的使
  • GWT - MVP 事件总线。创建多个处理程序

    我正在处理我继承的大型应用程序 并且遇到了一些最佳实践问题 每次用户导航到我们的客户编辑页面时 都会创建一个新的演示者并设置一个视图 有一个用于客户编辑的主演示者和一个主视图 主视图中还存在由主演示者的子演示者使用的子视图 在子演示者中 我
  • 3D numpy 数组转换为块对角矩阵

    我正在寻找一种将 nXaXb numpy 数组转换为块对角矩阵的方法 我已经遇到过scipy linalg block diag http docs scipy org doc scipy 0 13 0 reference generate
  • 内联块的水平滚动在 Firefox 中有效,但在 Chrome 中无效

    我已经在 Firefox 中实现了水平滚动 但在 Chrome 中不起作用 在 Firefox 中我遇到这种情况 其中 A B C D 是 div 但是当使用 Chrome 访问同一页面时 我看到的是 div 的结构如下 div class
  • 下载时进度条

    我有一个可进行应用内下载的应用程序 我通过以下方式成功下载了mp3文件 NSData data1 NSData dataWithContentsOfURL NSURL URLWithString http somefile mp3 data
  • “HTTP.SYS 中的 URL 保留”是什么意思?

    无法理解这句话的意思 论坛上的人们互相建议在 HTTP sys 中保留 url 但这意味着什么呢 它是做什么用的 它是如何运作的 这一切都来自于 HttpWebRequest uac 问题 一些 Win32 API 和 NET 框架组件 例
  • 百度的echarts - 填充两行之间的空间

    我想找到一种在 ECharts 中绘制两条线并填充它们之间的空间的方法 如下所示 这样每条线都有自己的颜色 根据线条的顺序 区域填充为一种颜色或另一种颜色 见图 有本地方法吗 我发现有些人在提到extensions 但没有人提供任何关于如何
  • 无法将 undefined 或 null 转换为 Next.js 中的对象

    嘿 我正在使用 Next js 和 next auth 构建一个登录页面 我还在providers数组中写过 nextauth js 但是当我运行代码 如下所示 时 import getProviders signIn from next
  • 我可以在移动应用程序的 PKCE Flow 中使用授权码吗?

    我知道 OAuth 2 0 授权代码与 PKCE 流程是 OAuth 的最佳实践 我们计划将它用于我们的 WEB 应用程序 但我不明白如何在不使用浏览器进行身份验证的情况下将此流程用于我的移动应用程序的本机用户体验 https medium
  • read() 函数的返回值是什么类型?

    我想从二进制文件中读取前 188 个字节 并检查第一个字符是否为0x47 代码如下 import os fp open try ts rb for i in range 100 buf fp read 188 if buf 0 x47 pr
  • Azure Bot Framework 模拟器错误 - System.ArgumentNullException:值不能为 null

    我需要一些帮助 我是 Azure 机器人框架开发新手 几周前使用 QnA 知识库创建了我的第一个聊天机器人 无论如何 我设法在 Azure 门户中创建了机器人 并且它运行良好 但我需要在 Bot Framework Emulator 使用
  • 从四元数查看矩阵

    我目前正在构建自己的四元数相机 据我所知 您只需要一个四元数即可完全指定相机的方向 如果我错了 请纠正我 那么 我将如何创建视图矩阵 顺便说一下 我使用 C 作为编程语言 任何帮助 将不胜感激 首先是一些注意事项 您会在网络上和有关该主题的
  • 如果一个 ViewController 中有两个 UITableView,一个带有自定义单元格引用,另一个是简单的,会怎么样?

    我正在尝试在一个 ViewController 中使用两个 UITableView 一个 UITableView 带有自定义单元格的引用 另一个很简单 我已经编写了这段代码 但它给了我控制错误可能会到达非 void 函数的末尾 所以给我建议
  • 如何在SQL中实现过滤系统?

    现在我计划在我的网站上添加一个过滤系统 例子 ID apple COLOR red TASTE sweet ORIGIN US ID mango COLOR yellow TASTE sweet ORIGIN MEXICO ID banan
  • 如何更改 powershell 脚本中哈希表列的标题

    我对 Powershell 脚本编写相当陌生 我正在编写一个 power shell 脚本 其中声明了一个哈希表 如下所示 a 1 b 2 my hash my hash Add a b 当我在 Powershell 中打印该表时 哈希表的
  • 在 Task.WhenAll 中执行多个任务时的 C# 线程

    如果在单个线程上执行以下操作 会发生什么 await Task WhenAll items select x gt SomeAsyncMethod x Where SomeAsyncMethod is defined like this w