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

循环创建子线程时访问主线程变量出现异常结果

多线程处理数据的变量捕获异常问题及修复

我正在编写一个读取大量动态数据的程序,希望通过多线程加速处理过程。了解线程调度由操作系统负责,但出现了异常结果,不知道解决方案。

原代码

int multiplier = rowCount / 20;

Debug.WriteLine("Row count is " + rowCount + "...");
Debug.WriteLine("Using 20 threads to complete job...");
Debug.WriteLine("Using " + multiplier + " as multiplier...");

for (int i = 1; i <= 20; i++) {
    new Thread(() => {
        int startRow = ((multiplier * i) - multiplier) + 1;
        int endRow = multiplier * i;
        
        if (i == 20) {
            endRow = rowCount;
        }
    
        Debug.WriteLine("    [THREAD " + i + "] start row: " + startRow + ", end row : " + endRow);

        for (int row = startRow; row <= endRow; row++) {
            for (int column = 1; column <= columnCount; column++) {
                //...data is read here
            }
        }
    }).Start();
}

实际异常结果

Row count is 2209...
Using 20 threads to complete job...
Using 110 as multiplier...
    [THREAD 4] start row: 331, end row : 440
    [THREAD 4] start row: 331, end row : 440
    [THREAD 5] start row: 441, end row : 550
    [THREAD 5] start row: 441, end row : 550
    [THREAD 6] start row: 551, end row : 660
    [THREAD 7] start row: 661, end row : 770
    [THREAD 9] start row: 881, end row : 990
    [THREAD 9] start row: 881, end row : 990
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 12] start row: 1211, end row : 1320
    [THREAD 14] start row: 1431, end row : 1540
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 16] start row: 1651, end row : 1760
    [THREAD 17] start row: 1761, end row : 1870
    [THREAD 19] start row: 1981, end row : 2090
    [THREAD 20] start row: 2091, end row : 2209
    [THREAD 20] start row: 2091, end row : 2209
    [THREAD 21] start row: 2201, end row : 2310

预期结果

Row count is 2209...
Using 20 threads to complete job...
Using 110 as multiplier...
    [THREAD 1] start row: 1, end row : 110
    [THREAD 2] start row: 111, end row : 220
    [THREAD 3] start row: 221, end row : 330
    [THREAD 4] start row: 331, end row : 440
    [THREAD 5] start row: 441, end row : 550
    [THREAD 6] start row: 551, end row : 660
    [THREAD 7] start row: 661, end row : 770
    [THREAD 8] start row: 771, end row : 880
    [THREAD 9] start row: 881, end row : 990
    [THREAD 10] start row: 991, end row : 1100
    [THREAD 11] start row: 1101, end row : 1210
    [THREAD 12] start row: 1211, end row : 1320
    [THREAD 13] start row: 1321, end row : 1430
    [THREAD 14] start row: 1431, end row : 1540
    [THREAD 15] start row: 1541, end row : 1650
    [THREAD 16] start row: 1651, end row : 1760
    [THREAD 17] start row: 1761, end row : 1870
    [THREAD 18] start row: 1871, end row : 1980
    [THREAD 19] start row: 1981, end row : 2090
    [THREAD 20] start row: 2091, end row : 2209

问题原因及修复方案

问题根源

这是闭包捕获循环变量的经典问题:lambda表达式捕获的是循环变量i的引用,而非当前迭代的具体值。主线程循环执行速度远快于子线程启动,当子线程开始运行时,i已经被后续迭代修改,甚至循环结束后i会变成21(因为循环条件是i<=20,最后一次迭代后i会自增到21),这就导致了重复线程编号、缺失编号甚至出现THREAD 21的异常情况。

修复方法

在循环内部创建一个局部变量,保存当前迭代的i值,让lambda捕获这个局部变量的副本(而非循环变量的引用)。这样每个线程拿到的都是独立、固定的值,不会被后续循环修改。

修改后的代码

int multiplier = rowCount / 20;

Debug.WriteLine("Row count is " + rowCount + "...");
Debug.WriteLine("Using 20 threads to complete job...");
Debug.WriteLine("Using " + multiplier + " as multiplier...");

for (int i = 1; i <= 20; i++) {
    // 用局部变量保存当前迭代的i值
    int currentThreadNum = i;
    new Thread(() => {
        int startRow = ((multiplier * currentThreadNum) - multiplier) + 1;
        int endRow = multiplier * currentThreadNum;
        
        if (currentThreadNum == 20) {
            endRow = rowCount;
        }
    
        Debug.WriteLine("    [THREAD " + currentThreadNum + "] start row: " + startRow + ", end row : " + endRow);

        for (int row = startRow; row <= endRow; row++) {
            for (int column = 1; column <= columnCount; column++) {
                //...data is read here
            }
        }
    }).Start();
}

运行修改后的代码,就能得到预期的线程分配结果,每个线程处理独立的行区间,不会出现重复或缺失的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:45:35