You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET中遍历1000-30万条记录,哪种循环性能更优?

循环性能测试异常原因解析

我之前以为for循环比foreach和LINQ更快,于是写了个控制台程序测试——传入含空值的字符串列表,分别用for、foreach、Parallel.ForEach、Parallel.For遍历找出空值索引并追加到字符串里。结果计时器统计发现foreach速度远快于其他循环,就算把List换成数组结果也一样,想搞懂这是为什么。

测试代码

static void Main(string[] args)
{
    List<string> value = new List<string>() { "1", "2", "3", "4", "5", "6",
        "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18",
        "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40",
        "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51",
        "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62",
        "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73",
        "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84",
        "85", "86", "87", "88", "89", "90", "91", "92", " ", "", "", "",
        "", "", "", "  " };

    string ColName = "EMPNO";

    var timer = new Stopwatch();
    timer.Start();
    string a = BlankDataInColumn(value, ColName);
    timer.Stop();
    TimeSpan timeTaken = timer.Elapsed;
    string foo = "Time taken: " + timeTaken.ToString(@"m\:ss\.fff");
    Console.WriteLine(foo);

    var timer1 = new Stopwatch();
    timer1.Start();
    string b = BlankDataInColumnforeach(value, ColName);
    timer1.Stop();
    TimeSpan timeTaken1 = timer1.Elapsed;
    string foo1 = "Time taken: " + timeTaken1.ToString(@"m\:ss\.fff");
    Console.WriteLine(foo1);

    var timer12 = new Stopwatch();
    timer12.Start();
    string c = BlankDataInColumnforeachParallel(value, ColName);
    timer12.Stop();
    TimeSpan timeTaken12 = timer12.Elapsed;
    string foo12 = "Time taken: " + timeTaken12.ToString(@"m\:ss\.fff");
    Console.WriteLine(foo12);

    var timer123 = new Stopwatch();
    timer123.Start();
    string d = BlankDataInColumnforParallel(value, ColName);
    timer123.Stop();
    TimeSpan timeTaken123 = timer123.Elapsed;
    string foo123 = "Time taken: " + timeTaken123.ToString(@"m\:ss\.fff");
    Console.WriteLine(foo123);
    Console.ReadLine();
}

public static string BlankDataInColumn(List<string> Column, string ColumnName)
{
    bool isBlank = false;
    StringBuilder rowNumber = new StringBuilder();
    for (int i = 0; i < Column.Count(); i++)
    {
        if (Column[i].HasNothing()) { rowNumber.Append($"{i + 1},"); isBlank = true; }
    }
    string BlankDataExist = isBlank ? $"The {ColumnName} have Blank Values in the following row number {rowNumber}" : null;
    return BlankDataExist;
}

public static string BlankDataInColumnforeach(List<string> Column,
    string ColumnName)
{
    bool isBlank = false;
    StringBuilder rowNumber = new StringBuilder();
    int i = 0;
    foreach (string col in Column)
    {
        i++;
        if (col.HasNothing()) { rowNumber.Append($"{i},"); isBlank = true; }
    }
    string BlankDataExist = isBlank ?
        $"The {ColumnName} have Blank Values in the following row number {rowNumber}"
        : null;
    return BlankDataExist;
}

public static string BlankDataInColumnforeachParallel(List<string> Column,
    string ColumnName)
{
    bool isBlank = false;
    StringBuilder rowNumber = new StringBuilder();
    int i = 0;
    Parallel.ForEach(Column, col =>
    {
        i++;
        if (col.HasNothing()) { rowNumber.Append($"{i},"); isBlank = true; }
    });
    string BlankDataExist = isBlank ?
        $"The {ColumnName} have Blank Values in the following row number {rowNumber}"
        : null;
    return BlankDataExist;
}

public static string BlankDataInColumnforParallel(List<string> Column,
    string ColumnName)
{
    bool isBlank = false;
    StringBuilder rowNumber = new StringBuilder();
    Parallel.For(0, Column.Count(), i =>
    {
        if (Column[i].HasNothing()) { rowNumber.Append($"{i + 1},"); isBlank = true; }
    });
    string BlankDataExist = isBlank ?
        $"The {ColumnName} have Blank Values in the following row number {rowNumber}"
        : null;
    return BlankDataExist;
}

原因解析

1. for循环的额外开销

你的for循环条件里用了Column.Count(),这是LINQ的扩展方法,而非直接访问List<T>的Count属性。虽然LINQ对ICollection<T>做了优化,但每次循环调用这个方法都会多一层包装开销。如果改成i < Column.Count(直接访问属性),for循环的性能会和foreach基本持平。

2. 并行循环的线程开销

Parallel.ForEach和Parallel.For的优势是处理大规模、计算密集型任务,你的测试数据量仅不到100个元素,线程创建、上下文切换的开销远远超过并行执行带来的收益。另外:

  • StringBuilder不是线程安全的,并行场景下调用Append会引发线程竞争,额外增加同步开销,甚至可能生成错误的结果;
  • BlankDataInColumnforeachParallel里的i++是线程不安全操作,多个线程同时修改会导致计数错误,这也是并行版本的隐性问题。

3. foreach的底层优化

.NET对foreach遍历List<T>做了深度优化:编译后会直接获取List的内部数组和长度,避免了IEnumerator的装箱操作(针对值类型枚举器),执行效率几乎和直接遍历数组的for循环一致。你的foreach实现没有多余开销,自然表现最优。

内容的提问来源于stack exchange,提问作者silver spark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 08:05:24