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();
}
}
}
运行:
由于各个任务内部延时不同,最先执行的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();
}
}
}
}
运行:
用上面的方法虽然有效,你可以看看,点击界面的时候,界面处鼠标指针会一直转圈,导致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();
}
}
}
}
运行:
用异步方式虽然界面不会卡住,但另一个问题来了,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("线程执行完毕");
});
}
运行:
效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:
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个线程执行");
});
}
运行:
到此为止,功能就实现了,这个需求在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();
运行:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/qq_38693757/article/details/119882627


猜你喜欢
- 当屏幕变为横屏的时候,系统会重新呼叫当前Activity的OnCreate方法,你可以把以下方法放在你的OnCreate中来检查当前的方向,
- 本文实例为大家分享了SpringMVC按Ctrl上传多个文件的具体实现代码,供大家参考,具体内容如下JSP页面注意:必须加入multiple
- 开篇点题:正则表达式方法效果=0(下面会提到效果)空行问题: VS:在使用过程中对于VS的自动整理不太满意,因为不会自动删除空行当出现这种情
- 关于java8 的stream排序用法这里不做多说,这里介绍下曾经在多字段排序时遇到过的一个坑。需求:需要根据id去分组,然后取出每组中行号
- 有人问我,怎么判断一个点是不是在多边形内,本来想着把这个多边形分成一个又一个三角形,如图, 然后判断这个点是不是在某个三角形中,如
- 一、什么是方法的重载?方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。调用时,会根据不同的参数自动匹配对应的方法。二、构成方
- 正文前: 1. IDEA内存优化(秒开的快感!!)因机器本身的配置而配置:\IntelliJ IDEA8\bin\idea.exe.vmop
- 如下所示:import java.security.MessageDigest;import java.security.NoSuchAlg
- 面试题一:判断下列程序运行结果package String_test;public class test_1 { public static
- 首先:因为工作需要,需要对接socket.io框架对接,所以目前只能使用netty-socketio。websocket是不支持对接sock
- 一、背景Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和
- 可以使用 Java 8 中的 Map.replaceAll() 方法将所有的值转为 String 类型:Map<String, Obj
- 本文介绍spring中自定义缓存resolver,通过自定义resolver,可以在spring的cache注解中增加附加处理。一、概述ca
- 秒杀功能秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全。界面比较简单,
- 同样该功能需要加载命名空间using System.Runtime.InteropServices;private const uint W
- 1.概念a.是个二叉树(每个节点最多有两个子节点)b.对于这棵树中的节点的节点值左子树中的所有节点值 < 根节点 < 右子树的所
- 一篇小白也能看懂的查找游戏物体的方式解析 – Unity 之 查找物体的几种方式。本文通过实际测试得出使用结论,大家进行简单记录,在使用时想
- PS:用了一下个推.感觉实现第三方应用的推送功能还是比较简单的.官方文档写的也非常的明确.学习内容:1.使用个推实现第三方应用的推送.所有的
- 不可变对象不可变(immutable): 即对象一旦被创建初始化后,它们的值就不能被改变,之后的每次改变都会产生一个新对象。var str=
- SpringBoot是什么?SpringBoot是spring家族中微型框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。