异步方法中的DataTables

时间:2014-03-26 21:59:16

标签: c# multithreading asynchronous datatable sqlbulkcopy

我创建了一些异步方法,并在这些方法中向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";
    }

就像我说我的问题是批量复制经常错过一些记录。如何批量复制所有记录而不会将其丢失为非线程安全调用?

2 个答案:

答案 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}}多次,每个数据表一次。