繁体   English   中英

使用延续任务的链式任务

[英]Chain tasks using continuation task

我正在尝试链接任务,所以一旦完成下一个任务,但 UI 不会更新。 我做了一门反应课程,其中一课是根据应用程序中的状态更改更新 UI,这就是我想要复制的内容。 更改应用程序的状态(基本上我将运行运行返回 bool 进行验证的方法),然后相应地更新 UI,我也在使用绑定,但由于某种原因它没有按预期运行,我不不知道我是否错误地遵循了文档。 我可以更改或修复什么以使其工作,并且在单个async Task<T>方法中使用多个任务实际上是否正确

public async Task<string> Connect_To_Ip()
{
    await Task.Run(() =>
    {
        details.State = "Connection To IP 127.0.01.258.....";
        Task.Delay(5000).Wait();
        }).ContinueWith(result => new Task(async () =>
        {
           await Task.Run(() =>
           {
               if (result.Status == TaskStatus.RanToCompletion)
               {
                  details.State = "Validating Card Number......";
                }                    
           });  
              
        }), TaskContinuationOptions.OnlyOnRanToCompletion);

     return details.State;
}     

我如何调用原始任务

Task connect = Connect_To_Ip();
await connect;

当您使用await时,您不需要Task.ContinueWith 等待操作之后的一切都是延续。 由于要在后台线程上进行验证,因此必须将更改发布回 UI 线程以更新 UI 元素,否则将产生跨线程异常。
这是因为无法从后台线程更新 UI 元素,除非通过INotifyPropertyChanged和数据绑定进行更新。
一种方法是使用Dispatcher在 UI 线程上调用 UI 操作或使用Progress<T>类,该类将始终在 UI 线程上执行注册的回调。

您的固定和简化代码可能如下例所示:

public async Task ValidateAsync()
{
  // Register the callback that updates the UI with the 'progressReporter'
  var progressReporter = new Progress<string>(message => details.State = message);

  // Execute the operation on a background thread
  await Task.Run(() => ConnectToIp(progressReporter));

  // Continuation starts here, after await
}

public async Task ConnectToIp(IProgress<string> progressReporter)
{
  progressReporter.Report("Connection To IP 127.0.01.258.....");

  await Task.Delay(5000);

  // Continuation starts here, after await

  progressReporter.Report("Validating Card Number......");
}

建议尽可能使用异步 API,而不是使用后台线程。 例如,要在不阻塞 UI 的情况下连接到服务器,您可以使用

HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");

我在 Windows 窗体中执行此操作(我打开了一个测试 Windows 窗体项目),但在 WPF 中应该大致相同。 我在表单上放置了一个按钮、一个标签和一个文本框。 然后我写了这段代码:

private async void button1_Click(object sender, EventArgs e)
{
    var result = await ValidateTextBox();
    if (result != null)
    {
        label1.Text = result;
        return;
    }
    var intResult = await ReadTextBox();
    label1.Text = intResult.ToString();
    await IncrementTextBox();
    intResult = await ReadTextBox();
    label1.Text = intResult.ToString();
}

private async Task<string> ValidateTextBox()
{
    await Task.Delay(2000);
    if (!int.TryParse(textBox1.Text, out _)) {
        return "Not Valid";
    }
    //otherwise
    return null;
}

private async Task<int> ReadTextBox()
{
    await Task.Delay(3000);
    if (!int.TryParse(textBox1.Text, out var result))
    {
        throw new Exception("Don't do that");
    }
    return result;
}

private async Task IncrementTextBox()
{
    await Task.Delay(3000);
    if (!int.TryParse(textBox1.Text, out var result))
    {
        throw new Exception("Don't do that");
    }
    textBox1.Text = (result + 1).ToString();
}

如果您在文本框中键入不是int的内容并按下按钮,几秒钟后,标签中将显示Not Valid

如果那里有一个数字,那么会有一个暂停,这个数字会显示在标签中。 然后再次暂停,文本框编号将增加 1。最后,再次暂停后,标签将显示增加的值。

请注意,这一切都在单个线程上运行。 但是,尽管存在所有延迟,UI 始终保持响应。

在每个函数的开头和按钮单击处理程序中每个等待之后的行上放置断点。 逐步完成(而不是进入)整个过程,您将看到等待如何创建延续

如何返回任务<object>在子任务上使用延续<div id="text_translate"><p>我喜欢尽量减少相互等待但不显示它的任务的调度 我将如何更改我的 TestOption1 以返回调用方法的任务?</p><p> <a href="https://i.stack.imgur.com/ua5wo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ua5wo.png" alt="任务继续"></a></p><pre> [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 &lt; 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 &lt; sw2.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 2 {1}", sw1, sw2); Assert.IsTrue(sw1.ElapsedTicks &lt; 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) =&gt; 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((_) =&gt; 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); } } }</pre><p> 我希望能够做这样的事情</p><pre> [TestMethod] public async Task TestContinueWithDelegate() { var data = await TestOptionReturn().ConfigureAwait(false); Assert.IsNotNull(data); } private static Task&lt;object&gt; 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); } }</pre><p> 由于数据库未打开,以下测试失败<a href="https://i.stack.imgur.com/fa7DO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fa7DO.png" alt="在此处输入图像描述"></a></p><p> [TestMethod] public async Task TestContinueWithDelegate() { using CancellationTokenSource cts = new CancellationTokenSource(); var 数据 = 等待 TestOptioDDL(cts.Token).ConfigureAwait(false); 使用 var reader = await TestOptionOutput(cts.Token).ConfigureAwait(false); 断言.IsNotNull(数据); 断言.IsTrue(reader.Read()); }</p><pre> private static Task&lt;object&gt; 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&lt;SqlDataReader&gt; 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); } }</pre></div></object>

[英]How to return Task<Object> using continuation on a child task

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 取消时,连续链中的哪个任务正在运行? 连续链接任务,然后运行并行任务 创建表示一系列延续任务的任务 使用Continuation链接任务的正确方法 使用LimitedConcurrencyLevelTask​​Scheduler时挂起的继续任务 任务继续 使用ConcurrentQueue监视一系列任务 如何返回任务<object>在子任务上使用延续<div id="text_translate"><p>我喜欢尽量减少相互等待但不显示它的任务的调度 我将如何更改我的 TestOption1 以返回调用方法的任务?</p><p> <a href="https://i.stack.imgur.com/ua5wo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ua5wo.png" alt="任务继续"></a></p><pre> [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 &lt; 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 &lt; sw2.ElapsedTicks, "Stopwatch 1 {0} Stopwatch 2 {1}", sw1, sw2); Assert.IsTrue(sw1.ElapsedTicks &lt; 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) =&gt; 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((_) =&gt; 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); } } }</pre><p> 我希望能够做这样的事情</p><pre> [TestMethod] public async Task TestContinueWithDelegate() { var data = await TestOptionReturn().ConfigureAwait(false); Assert.IsNotNull(data); } private static Task&lt;object&gt; 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); } }</pre><p> 由于数据库未打开,以下测试失败<a href="https://i.stack.imgur.com/fa7DO.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fa7DO.png" alt="在此处输入图像描述"></a></p><p> [TestMethod] public async Task TestContinueWithDelegate() { using CancellationTokenSource cts = new CancellationTokenSource(); var 数据 = 等待 TestOptioDDL(cts.Token).ConfigureAwait(false); 使用 var reader = await TestOptionOutput(cts.Token).ConfigureAwait(false); 断言.IsNotNull(数据); 断言.IsTrue(reader.Read()); }</p><pre> private static Task&lt;object&gt; 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&lt;SqlDataReader&gt; 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); } }</pre></div></object> 连续运行多个任务 具有连续性的简单并行任务
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM