不同 dbContext 上的异步任务。表示在此上下文中开始了第二次操作
Async tasks on different dbContexts. Says a second opreration started on this context
public static async void DoSomething(IEnumerable<IDbContext> dbContexts)
{
IEnumerator<IDbContext> dbContextEnumerator = dbContexts.GetEnumerator();
Task<ProjectSchema> projectSchemaTask = Task.Run(() => Core.Data.ProjectRead
.GetAll(dbContextEnumerator.Current)
.Where(a => a.PJrecid == pjRecId)
.Select(b => new ProjectSchema
{
PJtextid = b.PJtextid,
PJcustomerid = b.PJcustomerid,
PJininvoiceable = b.PJininvoiceable,
PJselfmanning = b.PJselfmanning,
PJcategory = b.PJcategory
})
.FirstOrDefault());
Task<int?> defaultActivitySchemeTask = projectSchemaTask.ContinueWith(antecedent =>
{
//This is where an exception may get thrown
return ProjectTypeRead.GetAll(dbContextEnumerator.Current)
.Where(a => a.PTid == antecedent.Result.PJcategory)
.Select(a => a.PTactivitySchemeID)
.FirstOrDefaultAsync().Result;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
Task<SomeModel> customerTask = projectSchemaTask.ContinueWith((antecedent) =>
{
//This is where an exception may get thrown
return GetCustomerDataAsync(antecedent.Result.PJcustomerid,
dbContextEnumerator.Current).Result;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
await Task.WhenAll(defaultActivitySchemeTask, customerTask);
}
我遇到的异常:
NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
大约每调用此函数 1/20 次就会抛出异常。而且异常似乎只发生在我用 ContinueWith()
.
链接任务时
当我为每个请求使用一个新的上下文时,怎么会有第二个上下文操作?
这只是我的代码示例。在实际代码中,我有 3 个父任务,每个父任务都附加了 1-5 个链式任务。
我做错了什么?
是的,这些天你基本上不应该使用 ContinueWith
;在 this 案例中,您将在同一任务上有两个延续(对于 defaultActivitySchemeTask
和 customerTask
);它们如何交互现在基本上是未定义的,并且将完全取决于两个异步流的工作方式,但你可以 绝对 在这里以重叠的异步操作结束(例如,在最简单的 "continuations are sequential",一旦第一个因为不完整而等待,第二个就会开始)。坦率地说,这应该是基于逻辑顺序 await
的代码,可能不会也使用 Task.Run
,但让我们暂时保留它:
ProjectSchema projectSchema = await Task.Run(() => ...);
int? defaultActivityScheme = await ... first bit
SomeModel customer = await ... second bit
我们不能同时执行两个从属查询而不冒在同一上下文中并发异步操作的风险。
在您的示例中,您似乎 运行 并行执行两个延续,因此它们可能会重叠,从而导致并发问题。 DbContext
不是线程安全的,因此您需要确保您的异步调用是顺序的。请记住,使用 async
/await
只会将您的代码变成状态机,因此您可以控制在进行下一个操作之前完成了哪些操作。单独使用异步方法并不能确保并行操作,但可以将您的操作包装在 Task.Run
中。所以你需要问问自己 Task.Run
是否真的需要(即在 ThreadPool 中安排工作)以使其并行。
您提到在您的真实代码中有 3 个父任务,每个父任务都附加了 1-5 个链式任务。如果 3 个父任务有单独的 DbContext,它们可以 运行 并行(每个都包含在 Task.Run
中),但它们的链式延续需要是顺序的(利用 async/await 关键字)。像这样:
public async Task DoWork()
{
var parentTask1 = Task.Run(ParentTask1);
var parentTask2 = Task.Run(ParentTask2);
var parentTask3 = Task.Run(ParentTask3);
await Task.WhenAll(parentTask1 , parentTask2, parentTask3);
}
private async Task ParentTask1()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
private async Task ParentTask2()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
private async Task ParentTask3()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
如果您的父任务在同一个 DbContext 上运行,为了避免并发,您将需要一个一个地等待它们(无需将它们包装到 Task.Run
):
public async Task DoWork()
{
await ParentTask1();
await ParentTask2();
await ParentTask3();
}
public static async void DoSomething(IEnumerable<IDbContext> dbContexts)
{
IEnumerator<IDbContext> dbContextEnumerator = dbContexts.GetEnumerator();
Task<ProjectSchema> projectSchemaTask = Task.Run(() => Core.Data.ProjectRead
.GetAll(dbContextEnumerator.Current)
.Where(a => a.PJrecid == pjRecId)
.Select(b => new ProjectSchema
{
PJtextid = b.PJtextid,
PJcustomerid = b.PJcustomerid,
PJininvoiceable = b.PJininvoiceable,
PJselfmanning = b.PJselfmanning,
PJcategory = b.PJcategory
})
.FirstOrDefault());
Task<int?> defaultActivitySchemeTask = projectSchemaTask.ContinueWith(antecedent =>
{
//This is where an exception may get thrown
return ProjectTypeRead.GetAll(dbContextEnumerator.Current)
.Where(a => a.PTid == antecedent.Result.PJcategory)
.Select(a => a.PTactivitySchemeID)
.FirstOrDefaultAsync().Result;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
Task<SomeModel> customerTask = projectSchemaTask.ContinueWith((antecedent) =>
{
//This is where an exception may get thrown
return GetCustomerDataAsync(antecedent.Result.PJcustomerid,
dbContextEnumerator.Current).Result;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
await Task.WhenAll(defaultActivitySchemeTask, customerTask);
}
我遇到的异常:
NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
大约每调用此函数 1/20 次就会抛出异常。而且异常似乎只发生在我用 ContinueWith()
.
当我为每个请求使用一个新的上下文时,怎么会有第二个上下文操作?
这只是我的代码示例。在实际代码中,我有 3 个父任务,每个父任务都附加了 1-5 个链式任务。
我做错了什么?
是的,这些天你基本上不应该使用 ContinueWith
;在 this 案例中,您将在同一任务上有两个延续(对于 defaultActivitySchemeTask
和 customerTask
);它们如何交互现在基本上是未定义的,并且将完全取决于两个异步流的工作方式,但你可以 绝对 在这里以重叠的异步操作结束(例如,在最简单的 "continuations are sequential",一旦第一个因为不完整而等待,第二个就会开始)。坦率地说,这应该是基于逻辑顺序 await
的代码,可能不会也使用 Task.Run
,但让我们暂时保留它:
ProjectSchema projectSchema = await Task.Run(() => ...);
int? defaultActivityScheme = await ... first bit
SomeModel customer = await ... second bit
我们不能同时执行两个从属查询而不冒在同一上下文中并发异步操作的风险。
在您的示例中,您似乎 运行 并行执行两个延续,因此它们可能会重叠,从而导致并发问题。 DbContext
不是线程安全的,因此您需要确保您的异步调用是顺序的。请记住,使用 async
/await
只会将您的代码变成状态机,因此您可以控制在进行下一个操作之前完成了哪些操作。单独使用异步方法并不能确保并行操作,但可以将您的操作包装在 Task.Run
中。所以你需要问问自己 Task.Run
是否真的需要(即在 ThreadPool 中安排工作)以使其并行。
您提到在您的真实代码中有 3 个父任务,每个父任务都附加了 1-5 个链式任务。如果 3 个父任务有单独的 DbContext,它们可以 运行 并行(每个都包含在 Task.Run
中),但它们的链式延续需要是顺序的(利用 async/await 关键字)。像这样:
public async Task DoWork()
{
var parentTask1 = Task.Run(ParentTask1);
var parentTask2 = Task.Run(ParentTask2);
var parentTask3 = Task.Run(ParentTask3);
await Task.WhenAll(parentTask1 , parentTask2, parentTask3);
}
private async Task ParentTask1()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
private async Task ParentTask2()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
private async Task ParentTask3()
{
// chained child asynchronous continuations
await Task.Delay(100);
await Task.Delay(100);
}
如果您的父任务在同一个 DbContext 上运行,为了避免并发,您将需要一个一个地等待它们(无需将它们包装到 Task.Run
):
public async Task DoWork()
{
await ParentTask1();
await ParentTask2();
await ParentTask3();
}