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