博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# Winform 基于Task的异步与延时执行
阅读量:5935 次
发布时间:2019-06-19

本文共 5061 字,大约阅读时间需要 16 分钟。

一、Task的机制

 
Task位于命名空间System.Threading.Tasks中,是.NET 4.0加入的新模块,其实现机制大致类似于线程池ThreadPool,不过对于ThreadPool来说Task的优势是很明显的:
 
ThreadPool的实现机制:(一对多)
 
1、应用程序拥有一个用于存放委托的全局队列;
2、使用ThreadPool.QueueUserWorkItem将新的委托加入到全局队列;
3、线程池中的多个线程按照先进先出的方式取出委托并执行。
 
Task的实现机制:(多对多)
 
1、应用程序拥有一个用于存放Task(包装的委托)的全局队列(存放主程序创建的Task,标记为了TaskCreationOptions.PreferFairness的Task),以及线程池中每个工作线程对应的本地队列(存放该工作线程创建的Task);
2、使用new Task()或Task.Factory.StartNew将新的Task加入到指定队列;
3、线程池中的多个线程按照优先处理本地队列,其次处理全局队列的方式取出Task并执行;
4、如果工作线程A发现本地队列为空(Task已处理完毕),那么A就会尝试去全局队列中获取Task,如果全局队列也为空,那么A就会到工作线程B的本地队列中“窃取”一个Task来执行,这种策略很明显的使得CPU更加充分的利用了并行执行。
 
参考资料:
 
 

二、Task的使用

 
创建Task并运行:
 
//新建一个Task            Task t1 = new Task(() => {                Console.WriteLine("Task完成!");            });            //启动Task            t1.Start();            Console.WriteLine("UI线程完成!");
上面是用new关键字创建,等同于如下使用Task.Factory(Task工厂)创建的方式:
 
//新建一个Task(Task工厂创建,自动启动)            Task t1 = Task.Factory.StartNew(() => {                Console.WriteLine("Task完成!");            });            Console.WriteLine("UI线程完成!");
这里为了简便使用了Lambda表达式(=> 为Lambda运算符),上面两部分代码都等同于如下:
 
void Test()            {                Console.WriteLine("Task完成!");            }            Action action = new Action(Test);            //新建一个Task            Task t1 = new Task(action);            //启动Task            t1.Start();            Console.WriteLine("UI线程完成!");
运行效果图:
 
 
Task的执行方式有同步和异步两种,上面的方式很明显是异步执行,我们可以看到做为主线程的UI线程是先一步执行完的。
 
那么要怎么样才能实现Task的同步执行呢?主要就这一个方法:Wait()!
 
代码如下:
 
//新建一个Task            Task t1 = new Task(() => {                Console.WriteLine("Task完成!");            });            //启动Task            t1.Start();            Console.WriteLine("UI线程开始等待!");//①            t1.Wait();            Console.WriteLine("UI线程完成!");//②
运行效果图:
 
 
主线程运行到t1.Wait()时,会读取t1的状态,当发现任务t1还未执行结束时,主线程便会阻塞在这个位置(只是阻塞在t1.Wait()位置,也就是说t1.Wait()之前的代码①照旧执行),当读取到t1的状态为已经执行结束时,主线程才会再次恢复执行,从t1.Wait()之后的位置②继续往下执行。
 
当然,当有多个任务都需要保持同步执行时,可以使用Task.WaitAll方法同时等待多个任务完成,代码如下:
 
//新建一个Task            Task t1 = new Task(() => {                Console.WriteLine("Task1完成!");            });            //新建一个Task            Task t2 = new Task(() => {                Console.WriteLine("Task2完成!");            });            //启动Task            t1.Start();            t2.Start();            Console.WriteLine("UI线程开始等待!");            //等待t1,t2都完成            Task.WaitAll(t1,t2);            Console.WriteLine("UI线程完成!");
运行效果图:
 
 
当然,对于Task的操作还有更多,这里对于我的需求无关紧要,所以不再列举,详情请参见MSDN的API文档:
 
 
 

三、基于Task的异步与延时

 
我在这里进行了如下封装:
 
///         /// 开始一个异步任务        ///         /// 异步任务执行委托        /// 异步任务执行完毕后的委托(会跳转回UI线程)        /// UI线程的控件        public void StartAsyncTask(Action taskAction, Action taskEndAction, Control control)        {            if (control == null)            {                return;            }            Task task = new Task(() => {                try                {                    taskAction();                    //返回UI线程                    control.Invoke(new Action(() =>                    {                        taskEndAction();                    }));                }                catch (Exception e)                {                    MessageBox.Show(e.Message);                }            });            task.Start();        }        ///         /// 开始一个延时任务        ///         /// 延时时长(秒)        /// 延时时间完毕之后执行的委托(会跳转回UI线程)        /// UI线程的控件        public void StartDelayTask(int DelayTime, Action taskEndAction, Control control)        {            if (control == null)            {                return;            }            Task task = new Task(() => {                try                {                    Thread.Sleep(DelayTime * 1000);                    //返回UI线程                    control.Invoke(new Action(() =>                    {                        taskEndAction();                    }));                }                catch (Exception e)                {                    MessageBox.Show(e.Message);                }            });            task.Start();        }
StartAsyncTask主要是执行一个异步操作,并在异步操作完成后执行指定的委托,这里因为Task的执行机制依然是多线程,由于winform的线程安全性使得非UI线程无法访问UI线程中的UI控件,所以在Task操作结束后执行的委托有必要返回到UI线程中,也就是说StartAsyncTask主要的功能就是在taskAction中执行一系列的异步运算,运算结束之后在taskEndAction中进行一些可视化的表现,比如给某某UI控件赋值。
 
StartDelayTask几乎等同于StartAsyncTask,只不过他更加的表现出来一种延时的特性,事实上在StartAsyncTask的taskAction中加入线程Sleep也就是StartDelayTask的效果了。
 
StartAsyncTask的使用,异步耗时操作:
 
//显示耗时等待界面(比如一串文字:正在加载,请稍等......)            WaitPage.ShowWait();            StartAsyncTask(                () => {                    //进行耗时操作......                },                () => {                    //耗时操作完成,隐藏耗时等待界面                    WaitPage.HideWait();                },                this);
StartDelayTask的使用,异步延时等待:
 
Console.WriteLine("我军将在三秒后发起反击!");            StartDelayTask(                //等待的秒数                3,                () => {                    //等待结束要做的事                    Console.WriteLine("我军开始反击!");                },                this);            for (int i = 0; i < 10; i++)            {                Console.WriteLine(String.Format("敌军第{0}轮进攻!",i));            }
运行效果图:
 
你可能感兴趣的文章
Android开发之获取设备的屏幕信息和px dp之间的转换
查看>>
.NET中的动态编译
查看>>
Android开发UI之Action Bar
查看>>
在Oracle中使用Guid
查看>>
iOS 在不添加库的情况下 通过抽象类来获取自己想要的方法
查看>>
罗将公布手机锤,我感到深深的内疚
查看>>
spark(1.1) mllib 源代码分析
查看>>
CentOSserverMysql主从复制集群结构
查看>>
Android实例-设置消息提醒(XE8+小米2)
查看>>
CSS之设置滚动条样式
查看>>
Activity启动模式 及 Intent Flags 与 栈 的关联分析
查看>>
Raspberry Pi Kernel Compilation 内核编译官方文档
查看>>
Jquery 数组操作
查看>>
不少专车司机考虑退出
查看>>
【Raspberry Pi】openwrt 路由
查看>>
java 操作excel 文件
查看>>
uva 11552 Fewest Flops 线性dp
查看>>
ZH奶酪:PHP抓取网页方法总结
查看>>
java并发编程学习: 原子变量(CAS)
查看>>
Javascript中的循环变量声明,到底应该放在哪儿?
查看>>