手记

C#语法——Task的应用

其实,我一直不怎么明白MSDN上的东西,怎么就那么涩会难懂。

那些大博客的文章,怎么就那么多涩会的名词,让人看都看不下去。

直到我看了杨毅老师的一篇球评,我才豁然开朗。

原来写文章写的好的那些,都希望自己做的内容可以挣钱。但一个人的专业领域是有限的。

做内容总有做完的时候。这个时候,自己已经出名了,那就要继续赚钱。那就要继续写下去。

就难免要写到自己不擅长的领域。

然后,我们就看到了那些似是而非的文章。

工工整整的文字,写着狗屁不通的逻辑。

只写不用,好家伙,能看懂才有鬼。

=======================================================================

废话少说,我们一起来看Task的应用。

Task的应用

​Task的MSDN的描述如下:

【Task类的表示单个操作不会返回一个值,通常以异步方式执行。

Task对象是一种的中心思想基于任务的异步模式首次引入.NETFramework 4 中。

因为由执行工作Task对象通常以异步方式执行线程池线程上而不是以同步方式在主应用程序线程中,可以使用Status属性,并将IsCanceled, IsCompleted,和IsFaulted属性,以确定任务的状态。

大多数情况下,lambda 表达式用于指定该任务所执行的工作量。

对于返回值的操作,您使用Task类。】

我对于Task的理解是这样的,Task是FrameWork4引进的新功能,他和ConCurrent命名空间一起被引进,用来替代Thread的使用。

根据我的使用,个人觉得,他确实比Thead的功能要丰富一些。

下面我们一起看一个最简单的例子:


using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace TaskConsole

{    class Program

    {        static void Main(string[] args)

        {            //当前线程标识            Console.WriteLine(Thread.CurrentThread.GetHashCode());



            Task task = new Task(run);

            Console.WriteLine("任务标识:" + task.GetHashCode() + ",状态:" + task.Status);//状态             task.Start();

            Console.WriteLine("任务标识:" + task.GetHashCode() + ",状态:" + task.Status);//状态            //任务完成后执行新任务

            Action ation = new Action(taskStart);

            task.ContinueWith(ation);





            Console.Read();

        }        public static void taskStart(Task task)

        {

            task = new Task(run);

            task.Start();            //如果注释上面两句话,则任务标识为 task.ContinueWith(ation)中task的任务

            Console.WriteLine("任务标识:" + task.GetHashCode() + ",状态:" + task.Status + ",当前线程:" + Thread.CurrentThread.GetHashCode());//状态             

        }        public static void run()

        { 

            Console.WriteLine("this is run");

        }

    }

}

一,task.GetHashCode(),是获取Task实例的唯一标识,每个Task都不一样。

测试发现,Task.GetHashCode()并不等于Thread.CurrentThread.GetHashCode()。

二,task.ContinueWith(),是任务结束后继续执行任务的方法,传一个Action,当任务结束后,触发该Action。

任务刚new出来的时候,task就又状态了,是Created,一但运行了,状态就是WaitingToRun。

运行结果如下:

根据MSDN的说明,Task.State是获取TaskState的枚举值,其枚举值代表的意义如下

|Canceled |该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。 有关更多信息,请参见任务取消。

| Created |该任务已初始化,但尚未被计划。

| Faulted |由于未处理异常的原因而完成的任务。

| RanToCompletion |已成功完成执行的任务。

| Running |该任务正在运行,但尚未完成。

| WaitingForActivation |该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。

| WaitingForChildrenToComplete |该任务已完成执行,正在隐式等待附加的子任务完成。

| WaitingToRun |该任务已被计划执行,但尚未开始执行。

任务嵌套

任务嵌套就是指在一个任务中又创建了一个任务。

而新建的任务就是子任务。在没有特殊声明的情况下,父子任务是一起运行的。

如SimpleNestedTask方法。

父子任务关联需要在创建子任务的时候,增加参数TaskCreationOptions.AttachedToParent。

将父子任务关联起来,此时父任务将等待子任务结束,才会完成。

如果使用Task创建任务,不需要使用TaskCreationOptions.AttachedToParent参数,因为只要父任务使用了子任务的返回结果,父任务自然就会等待子任务完成。


public class Program

    {

        static void Main(string[] args)

        { 

            WaitForSimpleNestedTask();

            Console.WriteLine("=====================================================");

            SimpleNestedTask();

            Thread.SpinWait(600000);//等待SimpleNestedTask结束 再运行

            Console.WriteLine("=====================================================");

            //SimpleNestedTaskAttachedToParent();

            Console.Read();

        }

        static void WaitForSimpleNestedTask()

        {

            var outer = Task.Factory.StartNew(() =>

            {

                Console.WriteLine("Outer1 task executing.");



                var nested = Task.Factory.StartNew(() =>

                {

                    Console.WriteLine("Nested1 task starting.");

                    Thread.SpinWait(5000000);

                    Console.WriteLine("Nested1 task completing.");

                    return 42;

                });



                // 父任务等待子任务完成

                return nested.Result;

                // 父任务不等待子任务完成

                //return 1;

            });

            //输出的时候使用了返回值outer.Result,所以自动等待父任务完成

            Console.WriteLine("Outer1 has returned {0}.", outer.Result);

        }

        static void SimpleNestedTask()

        {

            var parent = Task.Factory.StartNew(() =>

            {

                Console.WriteLine("Outer2 task executing.");



                var child = Task.Factory.StartNew(() =>

                {

                    Console.WriteLine("Nested2 task starting.");

                    Thread.SpinWait(500000);

                    Console.WriteLine("Nested2 task completing.");

                });

            });

            //等待父任务完成,再输出字符串,如果没有这个等待,这句话会先输出

            parent.Wait();

            Console.WriteLine("Outer2 has completed."); 

        }



        static void SimpleNestedTaskAttachedToParent()

        {

            var parent = Task.Factory.StartNew(() =>

            {

                Console.WriteLine("Outer3 task executing.");



                var child = Task.Factory.StartNew(() =>

                {

                    Console.WriteLine("Nested3 task starting.");

                    Thread.SpinWait(500000);

                    Console.WriteLine("Nested3 task completing.");

                }, TaskCreationOptions.AttachedToParent);

                //增加参数TaskCreationOptions.AttachedToParent,将子任务附加进父任务,这样子任务完成后,父任务才算完成。

            });

            

            //等待父任务完成,再输出字符串,如果没有这个等待,这句话会先输出

            parent.Wait();

            Console.WriteLine("Outer has completed.");

        } 

ConCurrent的线程安全的

因为,MSDN将在System.Collections.Concurrent命名空间下的集合,都称为线程安全的集合。

线程安全可以理解为可以被多个线程同时使用的集合,而且同时使用的时候是该集合的值是准确的。

下面举一个使用线程安全集合的例子,使用的是BlockingCollection。


using System;

using System.Collections.Concurrent;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading;

using System.Threading.Tasks;



namespace ParallelConsole

{

    class Program

    {

        

        //定义集合大小为51个,也可以不定义大小

        static BlockingCollection blocking = new BlockingCollection(51);

      

        static void Main(string[] args)

        {

            



            blocking = new BlockingCollection();

            Console.WriteLine("当前blocking为:" + blocking.IsCompleted + "设置了集合大小count一样是0,blocking.Count:" + blocking.Count());

            //当前线程标识

            Console.WriteLine(Thread.CurrentThread.GetHashCode());



            for (int i = 0; i < 3; i++)

            {

                ////如果添加到第3个,就设置添加完成,这时在添加就会抛异常

                //if (i == 3)

                //{

                //    blocking.CompleteAdding();

                //}

              

                Action action = new Action(run);

                Task task = new Task(action,i); 

                task.RunSynchronously(); 

            }

            Console.WriteLine("设置添加完成前:" + blocking.IsAddingCompleted);

            //设置添加完成后

            blocking.CompleteAdding();

            Console.WriteLine("设置添加完成后:" + blocking.IsAddingCompleted);

            #region 同步取 取3个

            //for (int i = 0; i < 3; i++)

            //{

            //    Action actionTake = new Action(take);

            //    actionTake();

            //}

            #endregion

            //并发读取

            

            #region 并发步取 取3个

            //blocking.IsCompleted 只有当集合被添加进内容,然后又都被取光了以后,他才会等于ture,否则都是false

            //当IsCompleted为ture时,就不能再取了否则会抛异常

            

            //同时取,结果是 

            //blocking:0

            //blocking:2

            //blocking:1

            if (!blocking.IsCompleted)//如果集合没取光

            {

                Action actionTake2 = new Action(take);

                Parallel.Invoke(actionTake2, actionTake2, actionTake2);

            }

            #endregion



            Console.WriteLine("当前blocking为:" + blocking.IsCompleted +  ",blocking数量为:" + blocking.Count());

            //数据被取光了以后, blocking.Count()为0

            Console.Read();

        }



        public static void take()

        {

            //同步取,blocking.Count()会真实的表现,而异步取,Count是不准确的,因为我取count的时候,可能集合已经又被取出数据了,测试10次肯定会出现不真实的情况

            Console.WriteLine("blocking:" + blocking.Take() + ",blocking数量为:" + blocking.Count());

        }

        public static void run(object i)

        {

            int currentI = int.Parse(i.ToString());

            blocking.TryAdd(currentI); 

        }

    }

}

Parallel

Parallel.Invoke(),并发调用Action,可以传多个Action,也可以传一个Action数据组。

Task

Task(Action,object),这是Task的构造方法,接收Action,object是Action的参数,。

task.RunSynchronously(),他是同步运行任务计划用的,同时他和task.Start()一样,也可以启动线程。

BlockingCollection集合

属性一:IsCompleted,他是表示集合是否有数据,只有当集合被添加进内容,然后又都被取光了以后,他才会等于ture,否则都是false。

属性一:BlockingCollection.IsAddingCompleted,表示是否添加完成。针对blocking.CompleteAdding()的使用,当调用了该方法IsAddingCompleted就为true。

方法一:BlockingCollection.blocking.CompleteAdding(),设置IsAddingCompleted用的。

方法二:BlockingCollection.Add,添加一个实体

方法三:BlockingCollection.TryAdd,添加一个实体,我这里用的是这个方法,区别是,如果添加重复项,他会引发InvalidOperationException这个异常。

方法四:BlockingCollection.Take,从集合中取一个值,注意,是真的取出来,取出来后,BlockingCollection.cout会减一。

运行结果如下:

0人推荐
随时随地看视频
慕课网APP