C# -Task.Run() 中线程池中的索引超出范围异常

Posted

技术标签:

【中文标题】C# -Task.Run() 中线程池中的索引超出范围异常【英文标题】:Index Out of Range Exception in Thread Pooling in C# -Task.Run() 【发布时间】:2020-04-30 01:48:31 【问题描述】:

当我从代码中删除线程池或取消注释 Console.WriteLine() 时,代码工作正常,但为了提高性能,我想在单独的任务上处理每个 DataTable 列。它抛出索引超出范围异常。

DataTable dt = new DataTable();
private async void btnExcel_Click(object sender, RoutedEventArgs e)
    
        dt = new DataTable("worksheet");
        dt.Columns.Add("Id");
        dt.Columns.Add("MobileNo");
        dt.Columns.Add("Name");
        dt.Columns.Add("Name1");
        dt.Columns.Add("Name2");
        dt.Columns.Add("Name3");
        dt.Columns.Add("Name4");

        for (int i = 0; i < 100; i++)
            dt.Rows.Add(i, "99999", "ABC" + i, "n1", "n2", "n3", "n4");

        //var tasksInFlight = new Task[dt.Columns.Count];
        var tasksInFlight = new List<Task>();

        for (int index = 0; index < dt.Columns.Count; index++)
        
            tasksInFlight.Add(updateDt(index, "col " + index));

        

        //await Task.Factory.ContinueWhenAll(tasksInFlight, cT =>  string a = "abc"; );

        await Task.WhenAll(tasksInFlight);

    


    public async Task updateDt(int colNum, string data)
    
        try
        
            Task t = Task.Run(() =>
            
            for (int i = 0; i < dt.Rows.Count; i++)
            
                    // Console.WriteLine("Col Num : " + colNum + "  i = " + i);
                    dt.Rows[i][colNum] = data;
                
            );

            await t;
        
        catch (Exception ex)
        
        
    

【问题讨论】:

【参考方案1】:

当你调用tasksInFlight.Add(updateDt(index, "col " + index)); 时,index 的值不会被读取;您只需存储一个稍后将执行的任务 - 当您调用 Task.WhenAll 时。这是在评估 index 的值时执行任务的时间。这发生在循环结束并且index 的值现在等于dt.Columns.Count,超出了数组的范围。

了解 C# 闭包here。

要修复它,您可以这样做:

for (int index = 0; index < dt.Columns.Count; index++)

    int tmpIndex = index;
    tasksInFlight.Add(updateDt(tmpIndex, "col " + tmpIndex));


编辑:经过进一步调查,事实证明 DataTable 不是线程安全的。

除了上面的修复,DataTable 应该被锁访问:

 lock (dt) 
  
     dt.Rows[i][colNum] = data; 
 

但是,除非检索实际数据以放入 DataTable CPU 密集型,否则在这种情况下,锁会消除并发性的所有好处。

【讨论】:

谢谢您,我尝试了您的解决方案,但仍然是同样的问题,有时代码运行顺利,但有时会出现索引超出范围异常。请帮忙。 在 dt.Rows[i][colNum] = data 处抛出异常; 超出范围 - i 还是 colNum? 当我调试并检查 'i' 和 'colNum' 值在有效范围内时。请帮忙 尝试记录异常值。

以上是关于C# -Task.Run() 中线程池中的索引超出范围异常的主要内容,如果未能解决你的问题,请参考以下文章

C# 理解阻塞 UI 和异步/等待与 Task.Run 的问题?

Windows 模拟和 TPL

C#使用异步操作时的注意要点(翻译)

c#线程池中的异常

C# WCF 服务越界数组索引

c# 怎么等待线程池中所有线程都运行结束在运行主线程