C#如何Task执行任务,等待任务完成

作者:熊思雨 时间:2022-03-06 11:31:31 

Task执行任务,等待任务完成

代码:

//任务
Func<int> Funcs = () =>
{
? ? Console.WriteLine("任务开始");
?? ?return 1 + 1;
};
?
//执行任务
Task<int> printRes = Task.Run(Funcs);
?
//等待任务完成
printRes.GetAwaiter().OnCompleted(() =>
{
? ? Console.WriteLine("异步执行结果:" + printRes.Result); ? ? ? ?
});

运行:

任务开始
异步执行结果:2

C# Task任务队列

需求

众所周知,方法体内代码是从上往下执行的,在我们工作中经常会遇到一些需要延时执行,但又必须按顺序来执行的需求。这要怎么解决呢。微软官方提供的Task API就是专门来解决这个问题的。那么下面就开始吧。

基本的Task用法

新建一个Winfrom项目

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 线程2
{
   public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

private void Form1_Load(object sender, EventArgs e)
       {
           Task task1 = new Task(() =>
           {
               Thread.Sleep(400);
               Console.WriteLine("task1");
           });
           Task task2 = new Task(() =>
           {
               Thread.Sleep(300);
               Console.WriteLine("task2");
           });
           Task task3 = new Task(() =>
           {
               Thread.Sleep(200);
               Console.WriteLine("task3");
           });
           Task task4 = new Task(() =>
           {
               Thread.Sleep(100);
               Console.WriteLine("task4");
           });
           task1.Start();
           task2.Start();
           task3.Start();
           task4.Start();
       }
   }
}

运行:

C#如何Task执行任务,等待任务完成

由于各个任务内部延时不同,最先执行的Task1,反而最后一个执行完,如果既要做延时操作,又要求从任务按顺序执行,要怎么解决呢?

让Task任务按顺序执行

修改代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 线程2
{
   public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

private List<Task> TaskList = new List<Task>();

private void Form1_Load(object sender, EventArgs e)
       {
           Task task1 = new Task(() =>
           {
               Thread.Sleep(400);
               Console.WriteLine("task1");
           });
           Task task2 = new Task(() =>
           {
               Thread.Sleep(300);
               Console.WriteLine("task2");
           });
           Task task3 = new Task(() =>
           {
               Thread.Sleep(200);
               Console.WriteLine("task3");
           });
           Task task4 = new Task(() =>
           {
               Thread.Sleep(100);
               Console.WriteLine("task4");
           });

TaskList.Add(task1);
           TaskList.Add(task2);
           TaskList.Add(task3);
           TaskList.Add(task4);

foreach (Task task in TaskList)
           {
               task.Start();
               task.Wait();
           }
       }
   }
}

运行:

C#如何Task执行任务,等待任务完成

用上面的方法虽然有效,你可以看看,点击界面的时候,界面处鼠标指针会一直转圈,导致winfrom界面卡住,无法操作,这是因为使用Thread.Sleep 导致主线程阻塞,下面就来解决UI界面卡死的问题。

使用异步委托解决UI界面卡死问题

代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 线程2
{
   public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

private List<Task> TaskList = new List<Task>();

private void Button_Calculate_Click(object sender, EventArgs e)
       {
           Task task1 = new Task(async () =>
           {
               await Task.Delay(TimeSpan.FromSeconds(4));
               Console.WriteLine("task1");
           });
           Task task2 = new Task(async () =>
           {
               await Task.Delay(TimeSpan.FromSeconds(3));
               Console.WriteLine("task2");
           });
           Task task3 = new Task(async () =>
           {
               await Task.Delay(TimeSpan.FromSeconds(2));
               Console.WriteLine("task3");
           });
           Task task4 = new Task(async () =>
           {
               await Task.Delay(TimeSpan.FromSeconds(1));
               Console.WriteLine("task4");
           });

TaskList.Add(task1);
           TaskList.Add(task2);
           TaskList.Add(task3);
           TaskList.Add(task4);

foreach (Task task in TaskList)
           {
               task.Start();
               task.Wait();
           }
       }
   }
}

运行:

C#如何Task执行任务,等待任务完成

用异步方式虽然界面不会卡住,但另一个问题来了,task.wait()方法似乎没有效果。里面的任务队列依然没有按顺序来执行。那么如何即使用异步执行,也不阻塞主线程,而且要任务按顺序来执行呢?

异步任务队列按顺序执行

代码:

private void Test()
{
   Task.Run(() =>
   {
       Task t1 = new Task(() => {
           Thread.Sleep(2000);
           Console.WriteLine("t1");
           num = 1;
       });
       t1.Start();
       t1.Wait();
       Task t2 = new Task(() => {
           Thread.Sleep(1000);
           Console.WriteLine("t2");
           num = 3;
       });
       t2.Start();
       t2.Wait();
       Console.WriteLine("线程执行完毕");
   });
}

运行:

C#如何Task执行任务,等待任务完成

效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:

private async void Test()
{
   await Task.Run(async () =>
   {
       await Task.Delay(4000);
       Trace.WriteLine("第1个线程执行");
   });
   await Task.Run(async () =>
   {
       await Task.Delay(3000);
       Trace.WriteLine("第2个线程执行");
   });
   await Task.Run(async () =>
   {
       await Task.Delay(2000);
       Trace.WriteLine("第3个线程执行");
   });
}

运行:

C#如何Task执行任务,等待任务完成

到此为止,功能就实现了,这个需求在Unity3d中使用协程很简单的几句就可以搞定,但在Winfrom等项目的开发中,确实有点繁琐。

封装任务队列

下面的代码我不认为是一个很好的写法,需要添加任务后,还得手动去调用,如果能添加到任务队列就不管了,让其自己自动按顺序来执行任务,岂不是更好,读者如果有兴趣自己去完善这个猜想。另外,在游戏开发中,比如RGP项目中,有专门的任务系统,它和我这个帖子的概念不能混为一谈,RPG任务系统更多的偏向数据的存取,来获取任务的完成状态。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Utils
{
   public class TaskQueue
   {
       /// <summary>
       /// 任务列表
       /// </summary>
       private List<Task> TaskList = null;
       /// <summary>
       /// 是否在执行任务中
       /// </summary>
       private bool isPerformTask = false;
       /// <summary>
       /// 执行完任务的回调
       /// </summary>
       public Action CallBack = null;

private static TaskQueue _instance = null;
       public static TaskQueue Instance
       {
           get
           {
               if (_instance == null)
                   _instance = new TaskQueue();
               return _instance;
           }
       }

/// <summary>
       /// 添加任务
       /// </summary>
       /// <param name="task"></param>
       public void AddTask(Task task)
       {
           if (isPerformTask)
           {
               Console.WriteLine("[TaskQueue]任务正在执行中,此时不能做赋值操作");
               return;
           }

if (task != null)
           {
               TaskList.Add(task);
           }
       }

/// <summary>
       /// 执行任务
       /// </summary>
       public void PerformTask()
       {
           if (isPerformTask)
           {
               Console.WriteLine("[TaskQueue]任务正在执行中,不可重复调用");
               return;
           }
           if (TaskList == null || TaskList.Count == 0)
           {
               Console.WriteLine("[TaskQueue]任务列表为空");
               return;
           }        

Task.Run(() =>
           {
               isPerformTask = true;

foreach (Task item in TaskList)
               {
                   item.Start();
                   item.Wait();
               }

TaskList.Clear();
               isPerformTask = false;

if (CallBack != null) CallBack();
           });
       }

private TaskQueue()
       {
           TaskList = new List<Task>();
       }
   }
}

调用:

Task task1 = new Task(() =>
{
   Thread.Sleep(1000);
   Console.WriteLine("t1");
});
Task task2 = new Task(() =>
{
   Thread.Sleep(2000);
   Console.WriteLine("t2");
});
Task task3 = new Task(() =>
{
   Console.WriteLine("t3");
});
Action callback = () =>
{
   Console.WriteLine("所有任务执行完成");
};
TaskQueue.Instance.AddTask(task1);
TaskQueue.Instance.AddTask(task2);
TaskQueue.Instance.AddTask(task3);
TaskQueue.Instance.CallBack = callback;
TaskQueue.Instance.PerformTask();

运行:

C#如何Task执行任务,等待任务完成

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。 

来源:https://blog.csdn.net/qq_38693757/article/details/119882627

标签:C#,Task,执行任务
0
投稿

猜你喜欢

  • SpringBoot访问静态资源的配置及顺序说明

    2022-07-18 13:53:31
  • 详解升级Android Studio3.0时遇到的几个问题

    2021-11-19 08:13:52
  • RabbitMQ交换机使用场景和消息可靠性总结分析

    2023-10-06 14:00:55
  • C#实现的字符串转MD5码函数实例

    2023-03-02 15:34:43
  • Java基础教程之HashMap迭代删除使用方法

    2023-10-04 02:15:32
  • 聊一聊SpringBoot服务监控机制

    2023-02-09 02:47:48
  • Spring自动注入失败的解决方法

    2022-08-13 03:41:31
  • 对C#中public、private、protect的区别说明

    2021-05-30 22:11:21
  • Java如何将大文件切割成小文件

    2022-12-27 07:40:40
  • spring AOP定义AfterThrowing增加处理实例分析

    2021-07-11 14:22:11
  • Springboot和bootstrap实现shiro权限控制配置过程

    2022-01-19 21:31:23
  • Android后端服务器的搭建方法

    2022-05-07 19:38:04
  • 分享java中设置代理的两种方式

    2023-10-28 10:48:52
  • JavaWeb通过IDEA配置Servlet操作流程详解

    2023-10-09 09:26:23
  • Kotlin协程的线程调度示例详解

    2023-12-26 20:19:56
  • 基于jdk动态代理和cglib动态代理实现及区别说明

    2022-04-11 00:32:44
  • C#中隐式运行CMD命令行窗口的方法

    2021-06-11 18:47:07
  • FeignClient如何通过配置变量调用配置文件url

    2023-05-07 08:19:25
  • 基于Spring的注解@Qualifier小结

    2022-12-20 23:17:50
  • SpringBoot通过自定义注解实现参数校验

    2023-09-21 21:11:02
  • asp之家 软件编程 m.aspxhome.com