1

首先,我不确定我是否在问一些愚蠢的问题,所以在开始之前道歉。

我有一个方法使用 SqlCommand 异步保存数据库中的实体集合,它返回一个表示异步操作的任务,到目前为止它运行良好。基本上它会为每个实体生成一个 SQL 命令和一堆参数,并将它们全部添加到 SqlCommand 实例中(参数已编号)

但是,如果我尝试插入很多实体,当参数计数达到 2100 时,SQL 操作就会失败,因为那是限制。然后我想分批拆分实体集合,并按顺序执行它们,然后返回一个直到所有子任务都完成后才会完成的任务。(应该是最后一个)

每个子任务都会得到一个 Int32,说明有多少行发生了变化,最终任务必须返回所有行的总和。所以所有的任务都是Task。如果其中一个失败,它们都必须“回滚”,因此它们必须共享连接和事务对象。

另外,我想确保我在 Task 和 SqlCommand 中正确使用了 I/O 完成端口,并且在 SQL Server 上的操作完成时没有线程等待/旋转,因为这个“保存”操作是从 ASP.NET MVC 中的异步控制器调用。

这里的正确方法是什么?

问候。

4

3 回答 3

1

如果您要拆分插入,但按顺序运行它们,则不需要为每个调用使用任务。你只需要正常的顺序代码。

创建一个启动 Task 并返回它的方法。在任务主体中,只需执行您想要的任何正常顺序代码。完成后,退出任务主体,单个任务将完成。

于 2012-04-20T14:20:56.493 回答
1

使用 C# 5(并假设基于任务的异步),这将非常简单:

async Task<IReadOnlyList<SomeType>> PerformUpdatesAsync(
    IEnumerable<AnotherType> data)
{
    var result = new List<SomeType>();

    foreach (var batch in data.SplitIntoBatches(BatchSize))
        result.Add(await PerformUpdateAsync(batch));

    return result;
}

在 C# 5 之前的版本中模拟异步foreach比较困难,但是您可以使用批处理队列和“递归”lambda 来实现它:

Task<SomeType[]> PerformUpdatesAsync(IEnumerable<AnotherType> data)
{
    var batches = new Queue<IEnumerable<AnotherType>>(
        data.SplitIntoBatches(BatchSize));

    var result = new List<SomeType>();

    var tcs = new TaskCompletionSource<SomeType[]>();

    AsyncCallback onEndUpdate = null;
    onEndUpdate =
        ar =>
        {
            result.Add(EndUpdate(ar));

            if (batches.Count == 0)
                tcs.SetResult(result.ToArray());
            else
                BeginUpdate(batches.Dequeue(), onEndUpdate, null);
        };

    BeginUpdate(batches.Dequeue(), onEndUpdate, null);

    return tcs.Task;
}

如果您不喜欢以这种方式使用闭包,您可以通过创建一个单独的类来做同样的事情,该类将在其字段中保存所有局部变量。

框架中没有SplitIntoBatches(),因此您必须自己编写(除非您已经这样做了)。

于 2012-04-20T16:55:43.220 回答
0

这是一个很好的问题。这是一些代码:

Task<int>[] batchTasks = ...;//start the batches here. each task should return the count of changed rows
Task<int> sumTask = Task.Factory.ContinueWhenAll(batchTasks, _ => {
 return batchTasks.Sum(x => x.Result);
});

这将为您提供各个批次的无阻塞总和。

启动批处理时,请务必使用 BeginExecuteReader 等异步 SQL API。然后,您将完全无阻塞。

于 2012-04-20T12:30:45.297 回答