簡體   English   中英

將線程安全集合轉換為DataTable的最佳方法?

[英]Best way to convert thread safe collection to DataTable?

所以這是場景:

我必須獲取一組數據,對其進行處理,構建一個對象,然后將這些對象插入數據庫中。

為了提高性能,我使用並行循環對數據處理進行多線程處理,並將對象存儲在CollectionBag列表中。

那部分工作正常。 但是,這里的問題是我現在需要獲取該列表,將其轉換為DataTable對象,然后將數據插入數據庫中。 這非常丑陋,我覺得我沒有以最好的方式做到這一點(下面的偽指令):

ConcurrentBag<FinalObject> bag = new ConcurrentBag<FinalObject>();

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;

Parallel.ForEach(allData, parallelOptions, dataObj =>
{   
    .... Process data ....

    bag.Add(theData);

    Thread.Sleep(100);
});

DataTable table = createTable();
foreach(FinalObject moveObj in bag) {
    table.Rows.Add(moveObj.x);
}

這是PLINQ(或Rx-因為它是基類庫的一部分,我將重點介紹PLINQ)的一個很好的候選人。

IEnumerable<FinalObject> bag = allData
    .AsParallel()
    .WithDegreeOfParallelism(Environment.ProcessorCount)
    .Select(dataObj =>
    {
        FinalObject theData = Process(dataObj);

        Thread.Sleep(100);

        return theData;
    });

DataTable table = createTable();

foreach (FinalObject moveObj in bag)
{
    table.Rows.Add(moveObj.x);
}

實際上,而不是通過Thread.Sleep限制循環,您應該進一步限制最大並行度,直到將CPU使用率降低到所需水平為止。

免責聲明:以下所有內容僅供娛樂之用,盡管它確實可以工作。

當然,您始終可以將其提升一個檔次,並生成一個完整的異步Parallel.ForEach實現,該實現允許您並行處理輸入並異步進行調節,而不會阻塞任何線程池線程。

async Task ParallelForEachAsync<TInput, TResult>(IEnumerable<TInput> input,
                                                 int maxDegreeOfParallelism,
                                                 Func<TInput, Task<TResult>> body,
                                                 Action<TResult> onCompleted)
{
    Queue<TInput> queue = new Queue<TInput>(input);

    if (queue.Count == 0) {
        return;
    }

    List<Task<TResult>> tasksInFlight = new List<Task<TResult>>(maxDegreeOfParallelism);

    do
    {
        while (tasksInFlight.Count < maxDegreeOfParallelism && queue.Count != 0)
        {
            TInput item = queue.Dequeue();
            Task<TResult> task = body(item);

            tasksInFlight.Add(task);
        }

        Task<TResult> completedTask = await Task.WhenAny(tasksInFlight).ConfigureAwait(false);

        tasksInFlight.Remove(completedTask);

        TResult result = completedTask.GetAwaiter().GetResult(); // We know the task has completed. No need for await.

        onCompleted(result);
    }
    while (queue.Count != 0 || tasksInFlight.Count != 0);
}

用法( 此處為完整小提琴 ):

async Task<DataTable> ProcessAllAsync(IEnumerable<InputObject> allData)
{
    DataTable table = CreateTable();
    int maxDegreeOfParallelism = Environment.ProcessorCount;

    await ParallelForEachAsync(
        allData,
        maxDegreeOfParallelism,
        // Loop body: these Tasks will run in parallel, up to {maxDegreeOfParallelism} at any given time.
        async dataObj =>
        {
            FinalObject o = await Task.Run(() => Process(dataObj)).ConfigureAwait(false); // Thread pool processing.

            await Task.Delay(100).ConfigureAwait(false); // Artificial throttling.

            return o;
        },
        // Completion handler: these will be executed one at a time, and can safely mutate shared state.
        moveObj => table.Rows.Add(moveObj.x)
    );

    return table;
}

struct InputObject
{
    public int x;
}

struct FinalObject
{
    public int x;
}

FinalObject Process(InputObject o)
{
    // Simulate synchronous work.
    Thread.Sleep(100);

    return new FinalObject { x = o.x };
}

行為相同,但沒有Thread.SleepConcurrentBag<T>

我認為類似這樣的東西應該可以提供更好的性能,看起來像object []比DataRow更好,因為您需要DataTable來獲取DataRow對象。

ConcurrentBag<object[]> bag = new ConcurrentBag<object[]>();

Parallel.ForEach(allData, 
    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, 
    dataObj =>
{
    object[] row = new object[colCount];

    //do processing

    bag.Add(row);

    Thread.Sleep(100);
});

DataTable table = createTable();
foreach (object[] row in bag)
{
    table.Rows.Add(row);
}

聽起來,通過使所有內容並行運行,您已經使事情變得相當復雜,但是如果您將DataRow對象存儲在包中而不是普通對象中,那么最后您可以使用DataTableExtensions輕松地從通用集合創建DataTable

var dataTable = bag.CopyToDataTable();

只需在項目中添加對System.Data.DataSetExtensions的引用即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM