我创建了一些异步方法,并在这些方法中向dataTable添加记录,然后在异步方法完成后批量复制dataTable。我现在发现dataTables不是线程安全的,这是我的问题的根源,因为我注意到一个或两个记录实际上没有插入。我有这样的事情:
private async void yea()
{
DataTable t = new DataTable();
//Fill the data table with its columns
IEnumerable<Task<string>> results = items.Select(q => AsyncFunction(q.id, t));
Task<string[]> allTasks = Task.WhenAll(results);
string[] allResults = await results;
using (SqlConnection conn = new SqlConnection(_connString))
{
conn.Open();
using (SqlBulkCopy bc = new SqlBulkCopy(conn))
{
bc.BatchSize = 1000;
bc.DestinationTableName = tableName;
bc.WriteToServer(t);
}
}
}
public async Task<string> AsyncFunction(int id, DataTable t)
{
//await another Async function
DataRow dr = t.NewRow();
dr["ID"] = id;
//Many More columns
t.Rows.Add(dr);
return "success";
}
就像我说我的问题是批量复制经常错过一些记录。如何批量复制所有记录而不会将其丢失为非线程安全调用?
答案 0 :(得分:2)
不要在异步方法中改变DataTable
。让异步方法计算行,返回它,然后在得到结果后添加所有行:
foreach(var row in await Task.WhenAll(items.Select(q => AsyncFunction(q.id, t))
t.Rows.Add(row);
答案 1 :(得分:1)
每个任务只做一个数据表,当你去上传时,你可以再次将它们连接在一起,你的代码不完整(例如Result
来自何处以及它是如何使用的)但是这里是如何做到这一点的基础知识,你需要适应你的代码。
DataTable t = new DataTable();
//Fill the data table with its columns
IEnumerable<Task<Result>> results = items.Select(q => AsyncFunction(q.id, t);
Task<NewResult[]> allTasks = Task.WhenAll(results); //This line is unnecessary with the code available.
NewResult[] allResults = await results;
using (SqlConnection conn = new SqlConnection(_connString))
{
conn.Open();
using (SqlBulkCopy bc = new SqlBulkCopy(conn))
{
bc.BatchSize = 1000;
bc.DestinationTableName = tableName;
//Joins all of the data rows from all of the generated tables in to a single array.
DataRow[] allRows = allResults.SelectMany(a=>a.LocalDataTable.AsEnumerable().ToArray();
bc.WriteToServer(allRows);
}
}
public async Task<NewResult> AsyncFunction(int id, DataTable template)
{
DataTable localDataTable = template.Clone(); //DataTable is thread safe for read operations like .Clone()
//Do some stuff
DataRow dr = t.NewRow();
dr["ID"] = id
//Many More columns
t.Rows.Add(dr);
//Be sure you have a "await" somewhere in that removed section or else this code will not be multi-threaded.
return new NewResult("success", localDataTable);
}
您还可以在每个类上调用.CreateDataReader()
,然后编写一个将多个IDataReaders
连接在一起的包装器,这样您就不需要分配额外的DataRow[]
或只需调用{{ 1}}多次,每个数据表一次。