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

目录
  • Task执行任务,等待任务完成
  • C# Task任务队列
    • 需求
    • 基本的Task用法
    • 让Task任务按顺序执行
    • 使用异步委托解决UI界面卡死问题
    • 异步任务队列按顺序执行
    • 封装任务队列

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();

运行:

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

(0)

相关推荐

  • C# task应用实例详解

    Task的应用 ​Task的MSDN的描述如下: [Task类的表示单个操作不会返回一个值,通常以异步方式执行. Task对象是一种的中心思想基于任务的异步模式首次引入.NETFramework 4 中. 因为由执行工作Task对象通常以异步方式执行线程池线程上而不是以同步方式在主应用程序线程中,可以使用Status属性,并将IsCanceled, IsCompleted,和IsFaulted属性,以确定任务的状态. 大多数情况下,lambda 表达式用于指定该任务所执行的工作量. 对于返回值的

  • C# 并行和多线程编程——认识和使用Task

    对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于"任务的编程模型"所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢? 任务和线程的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线

  • C#中的Task.WaitAll和Task.WaitAny方法介绍

    一.简介 Task.WaitAll:等待所有提供的 Task 对象完成执行过程. Task.WaitAny:等待提供的任一 Task 对象完成执行过程. 二.代码案例 Task.WaitAll 代码: class Program { public class DownLoadTest { Stopwatch watch = new Stopwatch(); public DownLoadTest() { watch.Start(); } public async Task DoRunTaskAs

  • C#并行编程之Task任务

    任务,基于线程池.其使我们对并行编程变得更简单,且不用关心底层是怎么实现的.System.Threading.Tasks.Task类是Task Programming Library(TPL)中最核心的一个类. 一.任务与线程 1:任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2:任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制. 我们用VS里面的“并行任务”看一看,快捷键Ctrl+D,K,

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

    目录 Task执行任务,等待任务完成 C# Task任务队列 需求 基本的Task用法 让Task任务按顺序执行 使用异步委托解决UI界面卡死问题 异步任务队列按顺序执行 封装任务队列 Task执行任务,等待任务完成 代码: //任务 Func<int> Funcs = () => {     Console.WriteLine("任务开始");     return 1 + 1; };   //执行任务 Task<int> printRes = Task

  • C#如何使用Task执行异步操作

    为什么要使用 Task 线程是创建并发的底层工具,因此具有一定的局限性. 没有简单的方法可以从联合(Join)线程得到"返回值".因此必须创建一些共享域.当抛出一个异常时,捕捉和处理异常也是麻烦的. 线程完成之后,无法再次启动该线程.相反,只能联合(Join)它(在进程阻塞当前线程). 任务是可组合的--使用延续将它们串联在一起.它们可以使用线程池减少启动延迟,而且它们可以通过TaskCompletionSource使用回调方法,避免多个线程同时等待I/O密集操作. Task 和 Th

  • PHP ajax 异步执行不等待执行结果的处理方法

    短地址生成应用中,要根据长地址生成网页快照,这个生成时间非瞬发,不可预估. 所以前台方面采用的方案一般为先展示生成的短地址,再定期AJAX轮查网页快照是否生成完毕. So,PHP代码这里做了如下处理: 复制代码 代码如下: // 这边服务器采用的是phantomjs 来生成网页快照 $cd        = '/home/emp/phpinstall/phantomjs-1.5.0/bin/phantomjs /home/emp/phpinstall/phantomjs/snap.js "'.$

  • C#如何使用Task类解决线程的等待问题

    目录 使用Task类解决线程的等待问题 Task类 用法 示例 小结 C#代码执行中等待10秒 使用Task类解决线程的等待问题 在任何的编程语言中,面对耗时任务时,我们都会有这样的需求:让任务执行一定时间,主任务进行等待,如果到时仍然完成不了,那么就不再等待. 比如一个常见的应用就是连接远程数据库,如果由于网络问题连接不上,那么只等待指定时间如3秒,然后就不再等待. 这样的代码如果自己写类来实现的话并不困难,然而实际上C#已经内置了Task类用于解决此问题. Task类 Task是C#中专门用

  • java使用任务架构执行任务调度示例

    复制代码 代码如下: package com.yao; import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.conc

  • 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主线程就不会阻塞,可以继续往下执行:等到后台线程执行完毕,再通知主线程,然后做出对应操作! 在C#中开启新线程比较简单 static void Main(string[]

  • 深入分析C# Task

    ​Task的MSDN的描述如下: [Task类的表示单个操作不会返回一个值,通常以异步方式执行. Task对象是一种的中心思想基于任务的异步模式首次引入.NETFramework 4 中. 因为由执行工作Task对象通常以异步方式执行线程池线程上而不是以同步方式在主应用程序线程中,可以使用Status属性,并将IsCanceled, IsCompleted,和IsFaulted属性,以确定任务的状态. 大多数情况下,lambda 表达式用于指定该任务所执行的工作量. 对于返回值的操作,您使用Ta

  • C# 并行和多线程编程——Task进阶知识

    一.Task的嵌套 Task中还可以再嵌套Task,Thread中能不能这样做,我只能说我是没这样写过.Task中的嵌套,我感觉其实也可以分开来写,不过嵌套起来会方便管理一点.Task中的嵌套分为两种,关联嵌套和非关联嵌套,就是说内层的Task和外层的Task是否有联系,下面我们编写代码先来看一下非关联嵌套,及内层Task和外层Task没有任何关系,还是在控制台程序下面,代码如下: static void Main(string[] args) { var pTask = Task.Factor

  • C#异步方法返回void与Task的区别详解

    C#异步方法返回void和Task的区别 如果异步(async关键字)方法有返回值,返回类型为T时,返回类型必然是 Task<T>. 但是如果没有返回值,异步方法的返回类型有2种,一个是返回 Task, 一个是返回 void: public async Task CountDownAsync(int count) { for (int i = count; i >= 0; i--) { await Task.Delay(1000); } } public async void Count

  • c#中task与thread区别及其使用的方法示例

    目录 1.什么是thread 2.什么是task 3.创建一个task任务有两种模式: 1.什么是thread 当我们提及多线程的时候会想到thread和threadpool,这都是异步操作,threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源.thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反. 总结:threadpool确实比thread性能优,但是两者都没有很好的api区控制,如果线程执行无响应就只能

随机推荐