C#中线程池中的索引超出范围异常-Task.Run()
Index Out of Range Exception in Thread Pooling in C# -Task.Run()
当我从代码中删除线程池或取消注释 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)
{
}
}
当你调用tasksInFlight.Add(updateDt(index, "col " + 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 密集型,否则在这种情况下锁会消除并发的所有好处。
当我从代码中删除线程池或取消注释 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)
{
}
}
当你调用tasksInFlight.Add(updateDt(index, "col " + 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 密集型,否则在这种情况下锁会消除并发的所有好处。