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

如何在多线程处理Excel DataRow时正确捕获行索引?

解决多线程处理DataRow时的行索引捕获问题

首先,你当前的实现存在线程安全隐患:多个线程共享同一个int i变量,i++不是原子操作,多个线程同时执行会导致索引值被覆盖、计数错误,日志里的行号会完全不准确。而且foreach遍历IEnumerable<DataRow>时本身没有内置索引,结合多线程场景,必须换一种线程安全的方式来获取每个行对应的原始索引。

核心思路

在进入多线程处理之前,先给每一行DataRow绑定它的原始行索引(对应Excel表格里的真实行号,包含表头计数),这样每个线程处理自己的行数据时,直接使用预绑定好的索引,不需要跨线程修改共享变量,彻底避免线程冲突。

修正后的实现步骤

  1. 预处理数据,绑定原始索引:把IEnumerable<DataRow>转换成包含行对象和原始索引的集合,同时跳过前5行表头(对应原始索引0-4,从索引5开始处理)。
  2. 移除共享变量i:每个线程只处理自己的行数据和对应的索引,不需要跨线程修改共享变量。
  3. 修复NHibernate Session的资源泄漏:用using块自动释放Session,确保线程结束后资源被正确回收。
  4. 修正代码拼写错误:比如你代码里的linha.ItemArray[2]应该是line.ItemArray[2](变量名拼写不一致)。

修正后的代码示例

private System.Threading.Tasks.Task TableProcessing(IEnumerable<DataRow> dataparam, User UserLogged)
{
    // 预处理:给每一行绑定原始Excel行号,跳过前5行表头
    var indexedRows = dataparam
        .Select((row, originalIndex) => new { Row = row, RowNumber = originalIndex + 1 }) // originalIndex从0开始,+1对应Excel的行号
        .Skip(5); // 跳过前5行表头(行号1-5)

    return Task.WhenAll(indexedRows.Select(rowWithIndex => Task.Run(() =>
    {
        // 使用using块确保NHibernate Session自动释放,避免资源泄漏
        using (var session = NhibernateHelper.HelpThreading().OpenSession())
        {
            NHibernate.Context.ThreadStaticSessionContext.Bind(session);
            var line = rowWithIndex.Row;
            var currentLineNumber = rowWithIndex.RowNumber; // 直接使用预绑定的行号,线程安全

            List<Process> listProcessExisting = new List<Process>();
            Process process = null;
            Interested interested = new Interested();

            // 按当前或之前的司法编号查询流程
            var currentProcessNum = line.ItemArray[1].ToString();
            if (!string.IsNullOrEmpty(currentProcessNum))
                process = aplProcess.consultPerNProcessER(currentProcessNum);
            
            if (process == null)
            {
                var prevProcessNum = line.ItemArray[2].ToString();
                if (!string.IsNullOrEmpty(prevProcessNum))
                    process = aplProcess.consultPerNProcessER(prevProcessNum); // 修复拼写错误:linha -> line
            }

            // 按CPF/CNPJ查询相关方
            var idValue = line.ItemArray[7].ToString();
            if (idValue.Length == 14)
                interested = aplInterested.ConsultPerCPF(AuxiliarCPF_CNPJ.DeformationCPF(idValue));
            else if (idValue.Length == 18)
                interested = aplInterested.ConsultPerCPF(HelpCPF_CNPJ.DeformationCNPJ(idValue));

            if (process != null)
            {
                // 查询流程关联的所有相关方
                List<ProcessInterested> listprocessinterested = aplProcessInterested.consultPerIdProcess(process.Code);
                if (listprocessinterested.Exists(o => o.Interested == interested))
                {
                    log.Append($"The interested {process.Interested.Name}, entered in the line {currentLineNumber} was added back to the process {process.NumberProcessjudicial} <br>");
                }
                setProcessInterested(process, interested, line, currentLineNumber);
            }
            else
            {
                // 创建新流程
                process = new Process();
                process.Interested = interested;
                // 可在此添加新流程的保存逻辑
            }
        }
    })));
}

关键改动说明

  • 线程安全的索引获取:通过Select((row, originalIndex) => ...)提前给每一行绑定行号,每个线程处理自己的行数据时直接使用currentLineNumber,完全避免了共享变量的竞争问题。
  • 资源安全:用using块包裹NHibernate Session,确保线程执行完毕后Session被正确关闭和释放,避免内存泄漏。
  • 代码健壮性:用string.IsNullOrEmpty替代直接判断Equals(""),逻辑更严谨;修正了变量拼写错误,避免运行时异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:47:19