当我从代码中删除线程池或取消注释Console.WriteLine()时,代码可以正常工作,但是为了提高性能,我想在单独的Task上处理每个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));
}
EDIT:经过进一步调查,结果证明DataTable不是线程安全的。
除了上述修复之外,还应该以锁定方式访问DataTable:
lock (dt)
{
dt.Rows[i][colNum] = data;
}
但是,除非在这种情况下,除非获取实际数据以占用CPU大量资源,否则锁将消除并发的所有好处。