如何在子任务上使用延续返回 Task<Object>
Posted
技术标签:
【中文标题】如何在子任务上使用延续返回 Task<Object>【英文标题】:How to return Task<Object> using continuation on a child task 【发布时间】:2020-07-21 22:15:06 【问题描述】:我喜欢尽量减少相互等待但不显示为一个的任务的调度 我将如何更改我的 TestOption1 以返回调用方法的任务?
[TestClass()]
public class SqlServerTests
public const string Membership = "Data Source=LocalHost;Initial Catalog=tempdb;Integrated Security=True;";
[TestMethod()]
public async Task ContinueWithTest()
using CancellationTokenSource cts = new CancellationTokenSource();
//warm up so pooling is enabled on all 3 methods
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "set nocount on";
con.Open();
cmd.ExecuteNonQuery();
var sw2 = System.Diagnostics.Stopwatch.StartNew();
await TestOption2(cts.Token).ConfigureAwait(false);
sw2.Stop();
//allow the benefit of the doubt for the slower and give it cashed plans
var sw3 = System.Diagnostics.Stopwatch.StartNew();
await TestOption3(cts.Token).ConfigureAwait(false);
sw3.Stop();
Assert.IsTrue(sw2.ElapsedTicks < sw3.ElapsedTicks, "Stopwatch 2 0 Stopwatch 3 1", sw2, sw3);
var sw1 = System.Diagnostics.Stopwatch.StartNew();
await TestOption1(cts.Token).ConfigureAwait(false);
sw1.Stop();
Console.WriteLine($"TestOption1: No internal awaits sw1.ElapsedTicks:N0 ticks");
Console.WriteLine($"TestOption2: 1x internal await sw2.ElapsedTicks:N0 ticks");
Console.WriteLine($"TestOption3: 2x internal await sw3.ElapsedTicks:N0 ticks");
Assert.IsTrue(sw1.ElapsedTicks < sw2.ElapsedTicks, "Stopwatch 1 0 Stopwatch 2 1", sw1, sw2);
Assert.IsTrue(sw1.ElapsedTicks < sw3.ElapsedTicks, "Stopwatch 1 0 Stopwatch 3 1", sw1, sw3);
private static Task TestOption1(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "set nocount on";
return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open....
.ContinueWith((t) => cmd.ExecuteNonQuery()
, cancellationToken
, continuationOptions: TaskContinuationOptions.ExecuteSynchronously
, scheduler: TaskScheduler.Default);
private static async Task TestOption2(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "set nocount on";
await con.OpenAsync(cancellationToken)
.ContinueWith((_) => cmd.ExecuteNonQuery(), cancellationToken).ConfigureAwait(false);
private static async Task TestOption3(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "set nocount on";
await con.OpenAsync(cancellationToken).ConfigureAwait(false);
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
我希望能够做这样的事情
[TestMethod]
public async Task TestContinueWithDelegate()
var data = await TestOptionReturn().ConfigureAwait(false);
Assert.IsNotNull(data);
private static Task<object> TestOptionReturn(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "Test1";
return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open....
.ContinueWith(delegate return cmd.ExecuteScalar();
, cancellationToken
, continuationOptions: TaskContinuationOptions.ExecuteSynchronously
, scheduler: TaskScheduler.Default);
由于数据库未打开,以下测试失败
[测试方法] 公共异步任务 TestContinueWithDelegate() 使用 CancellationTokenSource cts = new CancellationTokenSource(); var 数据 = 等待 TestOptioDDL(cts.Token).ConfigureAwait(false); 使用 var reader = await TestOptionOutput(cts.Token).ConfigureAwait(false); 断言.IsNotNull(数据); 断言.IsTrue(reader.Read());
private static Task<object> TestOptioDDL(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "TestOutput";
cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) IsNullable = true, Direction = System.Data.ParameterDirection.Output );
return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open....
.ContinueWith(delegate
cmd.ExecuteScalar();
return cmd.Parameters[0].Value;
, cancellationToken
, continuationOptions: TaskContinuationOptions.ExecuteSynchronously
, scheduler: TaskScheduler.Default);
private static Task<SqlDataReader> TestOptionOutput(CancellationToken cancellationToken = default)
using (var con = new SqlConnection(Membership))
using (var cmd = con.CreateCommand())
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "select * from sys.databases";
cmd.Parameters.Add(new SqlParameter("Data", System.Data.SqlDbType.DateTime) IsNullable = true, Direction = System.Data.ParameterDirection.Output );
return con.OpenAsync(cancellationToken)//fails as it does not wait for the db to open....
.ContinueWith(delegate
return cmd.ExecuteReader();
, cancellationToken
, continuationOptions: TaskContinuationOptions.ExecuteSynchronously
, scheduler: TaskScheduler.Default);
【问题讨论】:
你能详细描述一下你到底想做什么吗? 如果你已经确定你的连接在同步模式下工作,试试这个:var t1=con.OpenAsync(cancellationToken); t1.ContinueWith(delegate return cmd.ExecuteReader(); , cancellationToken , continuationOptions: TaskContinuationOptions.ExecuteSynchronously , scheduler: TaskScheduler.Default); return t1;
这能解决你的问题吗?
@SinaHoseinkhani,不,同样的错误
【参考方案1】:
TestOption3
是最好的选择。针对可维护性进行优化,而不是为了在 I/O 高度密集的任务上节省几毫秒。
也就是说,您的连接已关闭,因为您的连接正在处理before the tasks complete(可能在它甚至还没有打开之前!)。如果您要删除async
和await
,那么您需要处理重写您的方法,以便using
末尾的处理将在任务完成后运行。
【讨论】:
@Stehen 您能否在博客中发布一个示例,我相信有很多开发人员正在寻找这个。我的大部分任务都非常快,并且异步切换成为一个问题,特别是当服务器上的负载变大时 我很想知道您是如何断定问题在于异步切换的?请注意,您现有的测试时间无效;TestOption1
实际上并没有运行该命令,因为连接已关闭。
测试 1-3 被过度简化,因此所有人都可以尝试执行它,当运行 1 个生产代码时,我们在插入、更新和删除并且不需要事务时使用选项 1,很多非关键数据的日志记录,将其更改为较慢的内容,这真的很痛以上是关于如何在子任务上使用延续返回 Task<Object>的主要内容,如果未能解决你的问题,请参考以下文章