비동기 메서드의 DataTables

asynchronous c# datatable multithreading 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";
    }

마찬가지로 내 문제는 대용량 사본이 종종 몇 가지 레코드가 누락되었다는 것입니다. 모든 레코드를 비 스레드 안전 호출에 손실없이 대량 복사하려면 어떻게해야합니까?

수락 된 답변

작업 당 하나의 데이터 테이블 만 수행하면 업로드 할 때 다시 연결하여 코드가 완료되지 않을 수 있습니다 (예를 들어 어디에서 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[] 를 할당하거나 각 데이터 테이블에 한 번 SqlBulkCopy 여러 번 호출 할 필요가 없습니다.


인기 답변

비동기 메서드 내에서 DataTable 변경하지 마십시오. 비동기 메소드가 행을 계산하여 리턴하고, 결과를 얻은 후에 모든 행을 추가하십시오.

foreach(var row in await Task.WhenAll(items.Select(q => AsyncFunction(q.id, t))
    t.Rows.Add(row);


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.