Как вернуть задание <Object>, используя продолжение дочернего задания - PullRequest
0 голосов
/ 09 апреля 2020

Мне нравится минимизировать планирование задач, ожидающих друг друга, но не показывающих его. Как бы я изменил свой TestOption1, чтобы он возвращал задачу вызывающему методу?

Task continuations

    [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);
        }
    }

Следующие тесты не пройдены, поскольку база данных не открывается enter image description here

[TestMethod] publi c asyn c Task TestContinueWithDelegate () {using CancellationTokenSource cts = new CancellationTokenSource (); var data = await TestOptioDDL (cts.Token) .ConfigureAwait (false); использование var reader = await TestOptionOutput (cts.Token) .ConfigureAwait (false); Assert.IsNotNull (данные); Assert.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);
            }
        }

1 Ответ

2 голосов
/ 09 апреля 2020

TestOption3 - лучший выбор. Оптимизация для удобства обслуживания, а не для экономии нескольких миллисекунд для задачи с высокой степенью ввода-вывода.

При этом ваше соединение закрыто, поскольку ваше соединение удаляется до завершения задач (возможно, даже до того, как он откроется!). Если вы собираетесь удалить async и await, то вам нужно обработать переписывание вашего метода, чтобы удаление в конце using было выполнено после выполнения задач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...