[英]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.Sleep
和ConcurrentBag<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.