精华内容
下载资源
问答
  • C#多线程并行管理,通过Task实现,可对单个任务进行暂停,继续以及停止等操作,每个任务均有单独的进度条显示 同时执行的任务个数可以自行设置
  • C# Task

    千次阅读 2020-02-18 19:30:57
    Task作为C#异步的核心,类中的每个方法有必要学习一番,而部分重点方法更要尝试分析一下源码。 首先,Task位于System.Threading.Tasks命名空间下。 官方对其定义:Represents an asynchronous operation. 先看...

    Task作为C#异步的核心,类中的每个方法有必要学习一番,而部分重点方法更要尝试分析一下源码。

    首先,Task位于System.Threading.Tasks命名空间下。

    官方对其定义:Represents an asynchronous operation.

    先看一下Task的类注释,这里讲了很多重点。

    第一条注释:

    Task instances may be created in a variety of ways.The most common approach is by using the Task type's Factory property to retrieve a System.Threading.Tasks.TaskFactory instance that can be used to create tasks for serveral purposes

    也就是说,我们可以使用Task.Factory.StartNew这个方法的很多重载方式去创建一个适合应用场景的Task,下面是应用实例:

    static void Main(string[] args)
    {
         Console.WriteLine("Main1 Thread" + Thread.CurrentThread.ManagedThreadId);
         Task.Factory.StartNew(() => 
         {
             Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
         });
         Console.ReadLine();
    }
    
    //结果:
    Main1 Thread1
    Main2 Thread3

    第二条注释:

    The Task class also provides constructors that initialize the Task but that do not schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation and scheduling must be separated, the constructors may be used, and the task's Start() method may then be used to schedule the task for execution at a later time.

    意思就是

    如果你想建立一个Task并且立即执行它,使用Task.Factory.StartNew(),这样做性能更好

    如果你想建立一个Task,想在合适的时候执行它,那么使用Task构造器做初始化:

    static void Main(string[] args)
    {
        //初始化这个task,但不执行
        Task task = new Task(() => 
        {
           //doSomething
        });
        //做第一件事
        //做第二件事
        //做第N件事
        //之后再执行这个task
        task.Start();
        Task.Factory.StartNew(() => { });
    }

    这样更加灵活。

    第三条注释:

    All members of Task, except for Dispose(),are thread-safe and may be used from multiple threads concurrently.

    除了Dispose()方法,Task的其他所有成员都是线程安全的!

    第四条注释:

    For operations that return values, the System.Threading.Tasks.Task{TResult} class should be used.

    如果你想使用有返回值的Task,可以使用Task<Tresult>:

    Task<string> task = new Task<string>(() =>
    {
        //doSomething
        return "可以返回泛型类型的值";
    });
    Console.WriteLine("task的返回值是:" + task.Result);
    
    输出:
    可以返回泛型类型的值

    最后一条注释,表述了一些成员变量的作用:

    For developers implementing custom debuggers, several internal and private members of Task may be useful (these may change from release to release).
    ①The Int32 m_taskId field serves as the backing store for the Id property, however accessing this field directly from a debugger may be more efficient than accessing the same value through the property's getter method (the s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). 
    ②the Int32 m_stateFlags field stores information about the current lifecycle stage of the Task,information also accessible through the Status property. 
    ③The m_action System.Object field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the InternalWait method serves a potential marker for when a Task is entering a wait operation.

    ①直接使用m_taskId比使用ID的getter方法更好,因为其实可以从源码看到:

    public int Id 
    { 
        get 
        { 
            if (m_taskId == 0) 
            { 
                int newId = NewId(); 
                Interlocked.CompareExchange(ref m_taskId, newId, 0); 
            } 
            return m_taskId; 
        } 
    }

    Id的值是从m_taskId中取的。

    ②m_stateFlags和Status都记录了当前Task的生命周期阶段,而Status的值是从m_stateFlags中取的:

    public TaskStatus Status
    {
        get
        {
            TaskStatus rval;
    
            int sf = m_stateFlags;
     
            if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
            else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
            else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
            else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
            else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
            else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
            else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
            else rval = TaskStatus.Created;
     
            return rval;
         }
     }

    ③m_action存储Task的委托的引用,m_stateObject存储异步状态。然而当我想用vs开始一些测试时,发现根本调用不了这些成员,这里我就想到了我之前从《CLR via C#》当中看到的这么一段话:

    高级语言(包括C#)通常只公开了CLR全部功能的一个子集。然而IL汇编语言允许开发人员访问CLR的全部功能。

    所以,如果我想探寻这些变量的秘密,或许我嘚从IL入手(这是后面再回顾笔记的留言 :这里根本不是因为上面这些鬼扯,明明就是这两个变量是internal的访问权限而已...)。

    ④InternalWait()方法会在Wait()方法中被调用,作为判断task是否完成的一个标记。

    public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
    {
    	//......
        // Wait, and then return if we're still not done.
        if (!InternalWait(millisecondsTimeout, cancellationToken))
            return false;
        //......
    }

    说完了Task的重点注释之后,我们来看一下Task实现了哪些接口:

    IAsyncResult:

    encapsulate the results of an async operation 封装异步的操作结果

    成员:

    //判断异步操作是否完成
    bool IsCompleted 
    //该对象主要是有一些等待异步操作完成的动作
    WaitHandle AsyncWaitHandle
    //可设置的异步状态,比较灵活
    Object AsyncState
    //判断异步操作是否同步完成
    bool CompeltedSynchronously

    IDisposable:

    Interface for assisting with deterministic finalization.用于释放资源的接口.

    成员:

    //做资源释放的动作
    void Dispose()

    好了,基类看完了,再来看一下Task类的重点,属性与方法。

    首先,在学习时,应注重两点:

    ①作用是什么

    ②如何使用(实践操作)

    那么我们需要先知道去初始化Task对象,这里Task提供了一共8个构造器:

    public Task(Action action);
    public Task(Action action, CancellationToken cancellationToken);
    public Task(Action action, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state);
    public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state, CancellationToken cancellationToken);
    public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);

    不同的构造器实用于不同的场景,先来看第一个:

    public Task(Action action);

    ①以一个Action委托作为入参的Task构造器:

    static void Main(string[] args)
    {
        Action action = () => 
        {
            Console.WriteLine("The delegate method is executed");
        };
        Task task = new Task(action);
        task.Start();
        Console.ReadLine();
    }

    out:

    第一个比较简单,也比较常用,通常我们会将任务放到Action中然后给Task执行。

    ②Action<obejct>,object作为入参的Task构造器:

    public Task(Action<object> action, object state);
    static void Main(string[] args)
    {
        Action<object> action = (o) => 
        {
            Console.WriteLine("The delegate method is executed,state is :" + o);
        };
        object obj = "success";
        Task task = new Task(action,obj);
        task.Start();
        Console.ReadLine();
    }

    out:

    obj会被作为action的入参。

    ③Action,CancellationToken作为入参的Task构造器:

    public Task(Action action, CancellationToken cancellationToken); 
    static void Main(string[] args)
    {
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
    
        Action action = () =>
        {
    
            Console.WriteLine("I have been performed");
            ct.ThrowIfCancellationRequested();
            Console.WriteLine("Maybe I won't be executed");
        };
        Task task = new Task(action);
        task.Start();
        tokenSource.Cancel();
        Console.ReadLine();
    }

    out:

    主程序中,执行了Cancel方法,所以在Task执行任务时调用ThrowIfCancellationRequested方法检测到了Cancel操作,遂中止task,抛出异常,但因异常在Task线程,所以不会对主线程造成影响。

    这种方式创建的Task可以起到相应控制的作用。

    ④以Action,TaskCreationOptions作为Task入参的构造器:

    TaskCreationOptions是一个枚举类,一共有7个枚举值:

    参考:https://www.licc.tech/article?id=53

    public enum TaskCreationOptions
    {
        None = 0,
        PreferFairness = 1,
        LongRunning = 2,
        AttachedToParent = 4,
        DenyChildAttach = 8,
        HideScheduler = 16,
        RunContinuationsAsynchronously = 64
    }	

    TaskCreationOptions.None:一个一个来试一下吧:

    static void Main(string[] args)
    {
       Action action = () =>
       {
           Console.WriteLine("The delegate is executing");
       };
       TaskCreationOptions creationOptions = TaskCreationOptions.None;
       Task task = new Task(action, creationOptions);
       task.Start();
       Console.ReadLine();
    }

    TaskCreationOptions.PreferFairness:枚举值的描述是指定默认行为,所以可能这个枚举值设定和不设定应该对Task没有影响吧。

    提示TaskScheduler以一种公平的方式去执行Task,越早计划的Task会越早执行,相反,越晚计划的Task将会越晚执行。用例待补充。

    TaskCreationOptions.LongRunning:

    默认的TaskScheduler采用的是.NET线程池ThreadPool,它主要面向的是细粒度的小任务,其执行时间通常在毫秒级。线程池中的线程数与处理器的内核数有关,如果线程池中没有空闲的线程,那么后续的Task将会被阻塞。因此,如果事先知道一个Task的执行需要较长的时间,就需要使用TaskCreationOptions.LongRunning枚举指明。使用TaskCreationOptions.LongRunning创建的任务将会脱离线程池启动一个单独的线程来执行。

    先来看看本机有的cpu是几核的:

    现在设定一个任务,里面进行while(true){}循环让线程永远被占用,4核就跑5个task:

    static void Main(string[] args)
    {
        Action action = () =>
        {
            Console.WriteLine("The delegate is executing:" + Thread.CurrentThread.ManagedThreadId);
            while (true){ }
        };
        TaskCreationOptions creationOptions = TaskCreationOptions.None;
        //启动4个task
        for(int i = 0; i < 4; i++)
        {
    		Task.Run(action);
        }
    	//启动第5个task
        Task task = new Task(action, creationOptions);
        task.Start();
        Console.ReadLine();
    }

    结果:

    可以看到只有4个task被启动,第5个task被阻塞了。

    当使用第5个Task使用TaskCreationOptions.LongRunning之后

    static void Main(string[] args)
    {
        Action action = () =>
        {
            Console.WriteLine("The delegate is executing:" + Thread.CurrentThread.ManagedThreadId);
            while (true){ }
        };
        TaskCreationOptions creationOptions = TaskCreationOptions.LongRunning;
        //启动4个task
        for(int i = 0; i < 4; i++)
        {
    		Task.Run(action);
        }
    	//启动第5个task
        Task task = new Task(action, creationOptions);
        task.Start();
        Console.ReadLine();
    }

    结果:

    第5个task正常运行。

    TaskCreationOptions.AttachedToParent:

    正常情况下,如果在一个Task内部再建一个Task,两个Task位于2个线程,互相不会影响

    static void Main(string[] args)
    {
        TaskCreationOptions creationOptions = TaskCreationOptions.None;
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, creationOptions);
        });
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    可以看到,外层Task执行完毕,而后内层Task才执行完毕。

    现在来让外层Task等一下内层Task:

    static void Main(string[] args)
    {
        TaskCreationOptions creationOptions = TaskCreationOptions.AttachedToParent;
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, creationOptions);
        });
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    可以看到,外层Task会被wait住,等待内层Task执行完成,然后整个Task才会执行完成。

    另外,通过对内层Task设定TaskCreationOptions.AttachedToParent,内层Task抛出异常外层Task也可捕获处理。

    TaskCreationOptions.DenyChildAttach:

    如果内层设定了TaskCreationOptions.AttachedToParent让外层去等待自己执行完成,但外层不想等待,就可以给外层设定TaskCreationOptions.DenyChildAttach:

    static void Main(string[] args)
    {
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, TaskCreationOptions.AttachedToParent);
        },TaskCreationOptions.DenyChildAttach);
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    这样外层Task又会忽略内层Task的执行了。

    TaskCreationOptions.HideScheduler:

    //创建这个Task使用TaskScheduler.Current
    Task task = Task.Factory.StartNew(action);
    //创建这个Task使用TaskScheduler.Default
    Task task1 = Task.Factory.StartNew(action, TaskCreationOptions.HideScheduler);

    但如何证实具体使用的哪个TaskScheduler以及两个TaskScheduler的区别还不清楚。

    TaskCreationOptions.RunContinuationsAsynchronously 待补充

    剩余的其他构造方法便是Action,Action<object>,CancellationToken,TaskCreationOptions几个参数的不同组合了,到底运用哪个Task就要看实际的场景。

    现在再来看一下Task的所有属性:

    属性作用
    static int CurrentId 返回当前正在执行的Task的ID
    static TaskFactory Factory 创建Task的工厂类
    static Task CompletedTask 获取已成功完成的Task。
    TaskCreationOptions CreationOptions获取创建Task时指定的TaskCreationOptions 
    bool IsCompleted判断当前Task是否完成执行
    bool IsCanceled 判断当前Task是否由于被取消而完成
    TaskStatus Status获取当前Task的状态
    AggregateException Exception获取当前Task是否是因为过早结束而抛出的异常,如果当前Task成功的执行完成,那么会返回Null.
    int Id获取当前Task实例的ID
    object AsyncState获取当前Task的异步状态
    bool IsFaulted判断当前Task是否由于未处理异常而完成

    CurrentId:

    static void Main(string[] args)
    { 
        Task task = new Task(() =>
        {
             Console.WriteLine("CurrentId is " + Task.CurrentId);
         });
             Task task1 = new Task(() =>
             {
             Console.WriteLine("CurrentId is " + Task.CurrentId);
         });
         task.Start();
         task1.Start();
         Console.ReadLine();
    }

    结果:

    Id和CurrenctId看起来很像,并且也是同一个值,只是CurrentId只能在task的运行环境中获取。而Id就比较灵活了,因为是实例相关,只要可以使用该task实例对象的地方都可以获取到实例task的Id.

    static void Main(string[] args)
    { 
        Task task = new Task(() =>
        {
            Console.WriteLine("CurrentId is " + Task.CurrentId);
        });
        task.Start();
        Console.WriteLine("Id is " + task.Id);
        Console.ReadLine();
    }

    结果:

    Factory:

    Factory顾名思义,创建Task的工厂类:

    public class Program
    {
        static void Main(string[] args)
        {
            //1.最常见的方式
            Task<string> task = Task.Factory.StartNew(() => { return "I am the return value of task"; });
            //2.创建的task的委托方法运行条件是等到传入的异步操作完成
            Task.Factory.FromAsync(task, (asyncResult) => 
            {
                Task<string> task1 = (Task<string>)asyncResult;
                Console.WriteLine("第二种方式:" + task1.Result + "---------------------");
            });
            //3.当传入的一组task都完成之后执行委托方法
            Task<string> task2 = Task.Factory.StartNew(() => { return "I am the return value of task2"; });
            Task<string> task3 = Task.Factory.StartNew(() => { return "I am the return value of task3"; });
            Task<string>[] tasks = { task2, task3 };
            Task.Factory.ContinueWhenAll(tasks, (o) =>
             {
                 for (int i = 0; i < o.Length; i++)
                 {
                     Console.WriteLine("第三种方式:" + o[i].Result);
                 }
                  Console.WriteLine("-------------------------");
             });
            //4.传入的一组task中的任何一个完成,完成的那个task将继续执行委托方法
            Task.Factory.ContinueWhenAny(tasks, (o) =>
             {
                 Console.WriteLine("第四种方式:" + o.Result);
                 Console.WriteLine("-------------------------");
             });
            Console.ReadLine();
        }
    }

    结果:

     Task.CompletedTask:

    这个属性看起来像是拿到我们之前执行完成的Task,对吧?所以我的想法是构造许多Task,然后之后再调这个方法,得到之前构造中的Task中的一个:

    static void Main(string[] args)
    {
        Task.Run(() => { Console.WriteLine("task1:" + Task.CurrentId); });
        Task.Run(() => { Console.WriteLine("task2:" + Task.CurrentId); });
        //等待上面Task完成
        Thread.Sleep(100);
        Console.WriteLine("Task.CompletedTask id:" + Task.CompletedTask.Id);
        Console.ReadLine();
    }

    但是执行结果总是不尽人意:

    无论如何执行多少次,或者增加多少个task,总是得不到之前执行完成的Task。为什么呢?

    来看看源码吧:

    可以看到,如果是第一次调用这个属性,那么它会新建一个已经完成的Task,之后再调用也会返回同样的Task实例,和你执行的其他task没有任何关系,那么拿到这个Task有什么用呢?

    参考:https://stackoverflow.com/questions/44096253/await-task-completedtask-for-what/44096289

    这篇文章解释的很好,就是如果你想构造一个异步方法,但是现在又没有可以执行的异步逻辑,而又为了以后不为了新增异步逻辑而将同步方法重构为异步方法,那么就使用await Task.CompletedTask这个属性,当做一个占位符,说明这里将来会有异步逻辑的加入。所以这个属性是为了提升程序可扩展性而使用的。

    Task.CreationOptions:

    上面初始化Task时已经说过。

    Task.IsCompleted:

    static void Main(string[] args)
    {
       Task task = Task.Run(() => 
       {
           Thread.Sleep(50);
       });
       int count = 0;
       while (true)
       {
            if (count == 5 && task.IsCompleted)
            {
                Console.WriteLine("task已执行完成");
                break;
            }
            else
            {
                Console.WriteLine("在等待task完成之前先做其他事情");
                Thread.Sleep(10);
                if (count == 5)
                {
                    count = 0;
                }
                count++;
            }
        }
        Console.ReadLine();
    }

    结果:

    这个方法用于判断当前task是否已经执行完成,相较于其他的如Wait这类必须在等待task完成前阻塞线程的方法比起来,该属性的好处是线程可以留出时间给到其他程序,不用完全关注这个task是否完成,而是在需要的时候再去看它是否完成。

    Task.IsCanceled:

    我们先来看一下task会因异常中断而把IsCanceled属性置为true吗:

    static void Main(string[] args)
    {
        Task task = new Task(() =>
        {
            throw new Exception();
        });
        Thread.Sleep(100);
        Console.WriteLine("task因异常中断,task.IsCanceled值是:" + task.IsCanceled);
        Console.ReadLine();
    }

    结果:

    不会,那么我们从源码中对这个属性的标记描述可以知道它会在什么时候被设置为true:

    那就来试一下吧:

    ①在task开始执行之前标记cancellation:

    static void Main(string[] args)
    {
    
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
        Task task = new Task(() => { }, ct);
        //marked for cacellation before in started executing
        tokenSource.Cancel();
        task.Start();
        Console.WriteLine("task.IsCanceled:" + task.IsCanceled);
        Console.ReadLine();
    }

    不出所料:

    ②在程序中抛出OperationCanceledException或调用CancellationToken.ThrowIfCancellationRequested():

    static void Main(string[] args)
    {
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
        Task task = new Task(() => 
        {
            throw new OperationCanceledException();
            //or ct.ThrowIfCancellationRequested();
        }, ct);
        task.Start();
        tokenSource.Cancel();
        Thread.Sleep(10);
        Console.WriteLine("task.IsCanceled:" + task.IsCanceled);
        Console.ReadLine();
    }

    结果:

    Task.IsFaulted:

    随着task抛出未处理异常,在IsFaulted为真的情况下,Status会被设置为Faulted,Exception会记录异常。

    static void Main(string[] args)
    {
        Task task = new Task(() =>
        {
            throw new FileNotFoundException();
        });
        task.Start();
        task.ContinueWith((t) =>
        {
            Console.WriteLine("task.IsFaulted: " + t.IsFaulted);
            Console.WriteLine("task.Exception is null ? " + t.Exception == null);
            Console.WriteLine("task.Status:" + t.Status);
        });
        Console.ReadLine();
    }

    结果:

    Task.Exception:

    这个属性用于记录task抛出的异常,用IsFaulted那个例子即可:

    Task.AsyncState:

    最开始尝试直接执行一个task然后调用这个属性为null无果后,通过查看源码,得知可以从Task.Factory.FromAsync去设置这个值:

    这一步可以看到,AsyncState的值依赖于一个私有属性m_stateObject:

    这个私有属性又依赖state,通过一步一步最后找到了调用方:

    所以,就可以尝试用一下这个方法为这个属性赋值了:

    static void Main(string[] args)
    {
        Task task = Task.Factory.StartNew(() => { });
        Func<AsyncCallback, object, IAsyncResult> beginMethod = (asyncCallback, obj) => 
        {
            Console.WriteLine("beginMethod obj: " + obj);
            return task;
        };
        Action<IAsyncResult> endMethod = (asyncResult) => {};
        object state = "test asyncState"; 
        Task task1 = Task.Factory.FromAsync(beginMethod, endMethod, state);
        Console.WriteLine("task1 asyncState:" + task1.AsyncState);
        Console.ReadLine();
    }

    不出所料:

    好了,Task的属性说完了,现在来看一下它所包含的方法,几乎每个方法都有重载,其实只用看它重载方法中的一个就好了,其他就看场景选择:

    void Start()

    Starts the Task, scheduling it for execution to the current TaskScheduler.使用当前TaskScheduler去执行当前Task.

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("Current taskScheduler:" + TaskScheduler.Current.Id);
        });
        task.Start();
        Console.ReadLine();
    }

    结果:

    如果你不想使用默认的Scheduler去执行这个task,还可以使用带TaskScheduler参数的Start去自定义Scheduler。

    void Wait() //Waits for the Task to complete execution.等待task执行完成。

    static void Main(string[] args)
    {
        Task task = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        task.Wait();
        Console.WriteLine("当前task状态:" + task.Status);
        Console.ReadLine();
    }

    结果:

    使用这个方法,会阻塞式的等待task完成,如果想稍微灵活点,可以考虑使用带timeout参数的Wait方法。

    void RunSynchronously()//Runs the Task synchronously on the current TaskScheduler.使用scheduler同步运行当前Task.

    注意:

    使用该方法应保证task从未被执行过。

    根据MSDN介绍:

    Ordinarily, tasks are executed asynchronously on a thread pool thread and do not block the calling thread. Tasks executed by calling the RunSynchronously() method are associated with the current TaskScheduler and are run on the calling thread. If the target scheduler does not support running this task on the calling thread, the task will be scheduled for execution on the schedule, and the calling thread will block until the task has completed execution

    也就是说,使用这个方法会有两种方式同步执行task:

    ①直接使用当前线程执行

    ②另起一个线程执行,但会阻塞当前线程,相当于Start+Wait.

    看看例子:

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("使用RunSynchronously task会同步执行");
            Thread.Sleep(1000);
        });
        task.RunSynchronously();
        Console.WriteLine("当前task状态:" + task.Status);
        Console.ReadLine();
    }

    结果:

    Task ContinueWith(Action<Task> continuationAction) //Creates a continuation that executes asynchronously when the target Task completes. 当task执行完成继续执行continuationAction。

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("正在学习ContinueWith方法 " + 
                              Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(1000);
        });
        task.Start();
        Console.WriteLine("taskId:" + task.Id + ",task1 instance:" + task.GetHashCode());
        Task task1 = task.ContinueWith((t) =>
        {
            Console.WriteLine("我正在使用task继续执行任务 " + 
                              Thread.CurrentThread.ManagedThreadId);
        });
        Console.WriteLine("task1Id:" + task1.Id + ",task1 instance:" + task.GetHashCode());
        Console.ReadLine();
    }

    结果:

    通过结果可以知道:

    ①继续执行的任务和原task不被同一个线程执行

    ②虽是同一个task在继续执行任务,但ID不同了

    ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)

    https://blog.csdn.net/qq_38312617/article/details/104446307,这个文章有说到。

    YieldAwaitable Yield()

    对于官方解释,没有看太懂,大概意思好像是使用Yield,如果SynchronizationContext非null,那么await Task.Yield()之后代码会被列为优先级低的代码执行(依然会同步执行,只是暂时执行其他代码).如果是null,那么await Task.Yield()会被异步执行。

    这篇文章有相应解释,大概也印证了我看到的逻辑:

    https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield

    从代码看一下吧:

    static void Main(string[] args)
    {
        TestYield();
        Console.WriteLine("main thread:" + Thread.CurrentThread.ManagedThreadId);
        Console.ReadLine();
    }
    public async static void TestYield()
    {
        //Dosomething important
        Thread.Sleep(500);
        Console.WriteLine("TestYiled1 thread is " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine(SynchronizationContext.Current == null);
        await Task.Yield();
        //Unimportant
        Thread.Sleep(500);
        Console.WriteLine("TestYiled2 thread is " + Thread.CurrentThread.ManagedThreadId);
    }

    结果:

    果然,在SynchronizationContext为null的情况,后续代码在新的上下文中被执行。

    Task WhenAll(params Task[] tasks) //Creates a task that will complete when all of the Task objects in an array have completed. 建立一个task,等待所有task完成时该task完成

    如果你觉得一个一个处理等待task太麻烦,那么就可以使用这个方法去收集所有你要等待的task统一处理就好,就比如说,我有3个计算题要计算,我不需要你做完一个回复一次,这样没有必要,我只需要所有计算题做完就好:

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            int calc = 1 + 1; 
        });
        Task task2 = Task.Run(() => 
        {
            int calc = 2 + 2;
        });
        Task task3 = Task.Run(() =>
        {
            int calc = 3 + 3;
        });
        Task task4 = Task.WhenAll(task1, task2,task3);
        task4.Wait();
        if (task4.IsCompleted)
        {
            Console.WriteLine("task1、task2、task3 is completed calc");
        }
            Console.ReadLine();
        }

    结果:

    Task<Task> WhenAny(params Task[] tasks)//Creates a task that will complete when any of the supplied tasks have completed. 建立一个task,等待任务中的任何一个任务完成时完成。

    举个例子,你开启了3个抢票任务,有个抢票成功就可以通知你了,就可以利用这个方法:

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            Thread.Sleep(100);
            Console.WriteLine(" buy a ticket");
        });
        Task task2 = Task.Run(() => 
        {
            Thread.Sleep(200);
            Console.WriteLine(" buy a ticket");
        });
        Task task3 = Task.Run(() =>
        {
            Thread.Sleep(300);
            Console.WriteLine(" buy a ticket");
        });
        Task task4 = Task.WhenAny(task1, task2,task3);
        task4.Wait();
        if (task4.IsCompleted)
        {
             Console.WriteLine("Buy a ticket to complete~");
        }
             Console.ReadLine();
        }
    }

    结果:

    bool WaitAll(Task[] tasks, TimeSpan timeout)//Waits for all of the provided cancellable Task objects to complete execution within a specified time interval.在指定时间等待所有的task完成。

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        Task task2 = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        Task task3 = Task.Run(() =>
        {
             Thread.Sleep(1000);
        });
        Task[] tasks = { task1, task2, task3 };
        bool isCompleted = Task.WaitAll(tasks, TimeSpan.FromMilliseconds(2000));
        if (isCompleted)
        {
             Console.WriteLine("All of task is exeute completed");
        }
        Console.ReadLine();
     }

    结果:

    Task<TResult> Run<TResult>(Func<Task<TResult>> function)//Queues the specified work to run on the ThreadPool and returns a proxy for the Task(TResult) returned by function. 将任务放到线程池中排队等待运行,并且返回一个Task<TResult>的代理task.

    总之,Run方法就是拿来创建并执行任务的。

    static void Main(string[] args)
    {
        Func<Task<string>> func = () =>
        {
             Task<string> task = Task.Run(() => { return "0.0"; });
             return task;
        };
        Task<string> task1 = Task.Run(func);
        Console.WriteLine(task1.Result);
        Console.ReadLine(); 
    }

    结果:

    Task<TResult> FromResult<TResult>(TResult result)//Creates a Task`1 that's completed successfully with the specified result.//就是把TResult的值作为返回值task.Result的值。

    通过这篇文章可以知道这个方法的一些作用:

    https://stackoverflow.com/questions/19568280/what-is-the-use-for-task-fromresulttresult-in-c-sharp

    There are two common use cases I've found:

    1. When you're implementing an interface that allows asynchronous callers, but your implementation is synchronous.//可使用await Task.FromResult;设计一个异步方法,但方法内部是同步的
    2. When you're stubbing/mocking asynchronous code for testing.//做异步测试
    public async static void TestYield()
    {
        await Task<string>.FromResult("0.0");
    }

    Task<TResult> FromException<TResult>(Exception exception)//Creates a Task`1 that's completed with a specified exception.建立一个有指定异常的task.

    看文章:

    https://stackoverflow.com/questions/56444538/correct-usage-of-return-task-fromexception

    static void Main(string[] args)
    {
        Task task = TestYield();
        Console.WriteLine(task.Exception);
        Console.ReadLine();
    }
    public  static Task TestYield()
    {
        return Task.FromException<string>(new Exception("Something err"));
    }

    结果:

    Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)\\ Creates a Task that's completed due to cancellation with a specified cancellation token. 创建一个由于指定cancellation token取消而完成的task。

    与FromException类似。

    Task Delay(int millisecondsDelay)//Creates a task that completes after a time delay.创建延迟指定时间完成的task.

    static void Main(string[] args)
    {
        Task task = Task.Delay(1000);
        task.Wait();
        if (task.IsCompleted)
        {
            Console.WriteLine("task is execute completed");
        }
        Console.ReadLine();
    }

    结果:

    至此,Task中所有的继承、属性、方法都做了一遍了解,知道了基础用法,但还有很多方法不知道具体场景,这可能需要在以后在相应场景使用的了再回过头来补充。虽然思考场景是深入认识这个类的一个很好的方式,但无奈不能花费过多的时间在这一件事上,因为还有太多了不认识正等待着我去认识。

    展开全文
  • C# Task用法

    千次阅读 2019-02-22 14:38:28
    1、Task的优势  ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:  ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;  ◆ ThreadPool不支持线程执行的...

    1、Task的优势
      ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
      ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
      ◆ ThreadPool不支持线程执行的先后次序;
      以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式。
      以下是一个简单的任务示例:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Task t = new Task(() =>
                {
                    Console.WriteLine("任务开始工作……");
                    //模拟工作过程
                    Thread.Sleep(5000);
                });
                t.Start();
                t.ContinueWith((task) =>
                {
                    Console.WriteLine("任务完成,完成时候的状态为:");
                    Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                });
                Console.ReadKey();
            }
        }
    }

    2、Task的用法
      2.1、创建任务
      无返回值的方式
      方式1:
      var t1 = new Task(() => TaskMethod("Task 1"));
      t1.Start();
      Task.WaitAll(t1);//等待所有任务结束 
      注:
      任务的状态:
      Start之前为:Created
      Start之后为:WaitingToRun 

      方式2:
      Task.Run(() => TaskMethod("Task 2"));

      方式3:
      Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接异步的方法 
      或者
      var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
      Task.WaitAll(t3);//等待所有任务结束
      注:
      任务的状态:
      Start之前为:Running
      Start之后为:Running

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var t1 = new Task(() => TaskMethod("Task 1"));
                var t2 = new Task(() => TaskMethod("Task 2"));
                t2.Start();
                t1.Start();
                Task.WaitAll(t1, t2);
                Task.Run(() => TaskMethod("Task 3"));
                Task.Factory.StartNew(() => TaskMethod("Task 4"));
                //标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行。
                Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
    
                #region 常规的使用方式
                Console.WriteLine("主线程执行业务处理.");
                //创建任务
                Task task = new Task(() =>
                {
                    Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine(i);
                    }
                });
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                task.Start();
                Console.WriteLine("主线程执行其他处理");
                task.Wait();
                #endregion
    
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.ReadLine();
            }
    
            static void TaskMethod(string name)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            }
        }
    }

      async/await的实现方式:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            async static void AsyncFunction()
            {
                await Task.Delay(1);
                Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
                }
            }
    
            public static void Main()
            {
                Console.WriteLine("主线程执行业务处理.");
                AsyncFunction();
                Console.WriteLine("主线程执行其他处理");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(string.Format("Main:i={0}", i));
                }
                Console.ReadLine();
            }
        }
    }

      带返回值的方式
      方式4:
      Task<int> task = CreateTask("Task 1");
      task.Start(); 
      int result = task.Result;

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static Task<int> CreateTask(string name)
            {
                return new Task<int>(() => TaskMethod(name));
            }
    
            static void Main(string[] args)
            {
                TaskMethod("Main Thread Task");
                Task<int> task = CreateTask("Task 1");
                task.Start();
                int result = task.Result;
                Console.WriteLine("Task 1 Result is: {0}", result);
    
                task = CreateTask("Task 2");
                //该任务会运行在主线程中
                task.RunSynchronously();
                result = task.Result;
                Console.WriteLine("Task 2 Result is: {0}", result);
    
                task = CreateTask("Task 3");
                Console.WriteLine(task.Status);
                task.Start();
    
                while (!task.IsCompleted)
                {
                    Console.WriteLine(task.Status);
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                }
    
                Console.WriteLine(task.Status);
                result = task.Result;
                Console.WriteLine("Task 3 Result is: {0}", result);
    
                #region 常规使用方式
                //创建任务
                Task<int> getsumtask = new Task<int>(() => Getsum());
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                getsumtask.Start();
                Console.WriteLine("主线程执行其他处理");
                //等待任务的完成执行过程。
                getsumtask.Wait();
                //获得任务的执行结果
                Console.WriteLine("任务执行结果:{0}", getsumtask.Result.ToString());
                #endregion
            }
    
            static int TaskMethod(string name)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(2));
                return 42;
            }
    
            static int Getsum()
            {
                int sum = 0;
                Console.WriteLine("使用Task执行异步操作.");
                for (int i = 0; i < 100; i++)
                {
                    sum += i;
                }
                return sum;
            }
        }
    }

        async/await的实现:

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                var ret1 = AsyncGetsum();
                Console.WriteLine("主线程执行其他处理");
                for (int i = 1; i <= 3; i++)
                    Console.WriteLine("Call Main()");
                int result = ret1.Result;                  //阻塞主线程
                Console.WriteLine("任务执行结果:{0}", result);
            }
    
            async static Task<int> AsyncGetsum()
            {
                await Task.Delay(1);
                int sum = 0;
                Console.WriteLine("使用Task执行异步操作.");
                for (int i = 0; i < 100; i++)
                {
                    sum += i;
                }
                return sum;
            }
        }
    }

      2.2、组合任务.ContinueWith
       简单Demo:

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                //创建一个任务
                Task<int> task = new Task<int>(() =>
                {
                    int sum = 0;
                    Console.WriteLine("使用Task执行异步操作.");
                    for (int i = 0; i < 100; i++)
                    {
                        sum += i;
                    }
                    return sum;
                });
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                task.Start();
                Console.WriteLine("主线程执行其他处理");
                //任务完成时执行处理。
                Task cwt = task.ContinueWith(t =>
                {
                    Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
                });
                task.Wait();
                cwt.Wait();
            }
        }
    }

       任务的串行:

    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                ConcurrentStack<int> stack = new ConcurrentStack<int>();
    
                //t1先串行
                var t1 = Task.Factory.StartNew(() =>
                {
                    stack.Push(1);
                    stack.Push(2);
                });
    
                //t2,t3并行执行
                var t2 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("Task t2 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
                });
    
                //t2,t3并行执行
                var t3 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("Task t3 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
                });
    
                //等待t2和t3执行完
                Task.WaitAll(t2, t3);
    
                //t7串行执行
                var t4 = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("当前集合元素个数:{0},Thread id {1}", stack.Count, Thread.CurrentThread.ManagedThreadId);
                });
                t4.Wait();
            }
        }
    }

      子任务:

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                Task<string[]> parent = new Task<string[]>(state =>
                {
                    Console.WriteLine(state);
                    string[] result = new string[2];
                    //创建并启动子任务
                    new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
                    new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
                    return result;
                }, "我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
                //任务处理完成后执行的操作
                parent.ContinueWith(t =>
                {
                    Array.ForEach(t.Result, r => Console.WriteLine(r));
                });
                //启动父任务
                parent.Start();
                //等待任务结束 Wait只能等待父线程结束,没办法等到父线程的ContinueWith结束
                //parent.Wait();
                Console.ReadLine();
    
            }
        }
    }

      动态并行(TaskCreationOptions.AttachedToParent) 父任务等待所有子任务完成后 整个任务才算完成

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Node
        {
            public Node Left { get; set; }
            public Node Right { get; set; }
            public string Text { get; set; }
        }
    
    
        class Program
        {
            static Node GetNode()
            {
                Node root = new Node
                {
                    Left = new Node
                    {
                        Left = new Node
                        {
                            Text = "L-L"
                        },
                        Right = new Node
                        {
                            Text = "L-R"
                        },
                        Text = "L"
                    },
                    Right = new Node
                    {
                        Left = new Node
                        {
                            Text = "R-L"
                        },
                        Right = new Node
                        {
                            Text = "R-R"
                        },
                        Text = "R"
                    },
                    Text = "Root"
                };
                return root;
            }
    
            static void Main(string[] args)
            {
                Node root = GetNode();
                DisplayTree(root);
            }
    
            static void DisplayTree(Node root)
            {
                var task = Task.Factory.StartNew(() => DisplayNode(root),
                                                CancellationToken.None,
                                                TaskCreationOptions.None,
                                                TaskScheduler.Default);
                task.Wait();
            }
    
            static void DisplayNode(Node current)
            {
    
                if (current.Left != null)
                    Task.Factory.StartNew(() => DisplayNode(current.Left),
                                                CancellationToken.None,
                                                TaskCreationOptions.AttachedToParent,
                                                TaskScheduler.Default);
                if (current.Right != null)
                    Task.Factory.StartNew(() => DisplayNode(current.Right),
                                                CancellationToken.None,
                                                TaskCreationOptions.AttachedToParent,
                                                TaskScheduler.Default);
                Console.WriteLine("当前节点的值为{0};处理的ThreadId={1}", current.Text, Thread.CurrentThread.ManagedThreadId);
            }
        }
    }

      2.3、取消任务 CancellationTokenSource

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private static int TaskMethod(string name, int seconds, CancellationToken token)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                for (int i = 0; i < seconds; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    if (token.IsCancellationRequested) return -1;
                }
                return 42 * seconds;
            }
    
            private static void Main(string[] args)
            {
                var cts = new CancellationTokenSource();
                var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
                Console.WriteLine(longTask.Status);
                cts.Cancel();
                Console.WriteLine(longTask.Status);
                Console.WriteLine("First task has been cancelled before execution");
                cts = new CancellationTokenSource();
                longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
                longTask.Start();
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine(longTask.Status);
                }
                cts.Cancel();
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine(longTask.Status);
                }
    
                Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
            }
        }
    }

      2.4、处理任务中的异常
      单个任务:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static int TaskMethod(string name, int seconds)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                throw new Exception("Boom!");
                return 42 * seconds;
            }
    
            static void Main(string[] args)
            {
                try
                {
                    Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
                    int result = task.GetAwaiter().GetResult();
                    Console.WriteLine("Result: {0}", result);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
                }
                Console.WriteLine("----------------------------------------------");
                Console.WriteLine();
            }
        }
    }

      多个任务:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static int TaskMethod(string name, int seconds)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                throw new Exception(string.Format("Task {0} Boom!", name));
                return 42 * seconds;
            }
    
    
            public static void Main(string[] args)
            {
                try
                {
                    var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
                    var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
                    var complexTask = Task.WhenAll(t1, t2);
                    var exceptionHandler = complexTask.ContinueWith(t =>
                            Console.WriteLine("Result: {0}", t.Result),
                            TaskContinuationOptions.OnlyOnFaulted
                        );
                    t1.Start();
                    t2.Start();
                    Task.WaitAll(t1, t2);
                }
                catch (AggregateException ex)
                {
                    ex.Handle(exception =>
                    {
                        Console.WriteLine(exception.Message);
                        return true;
                    });
                }
            }
        }
    }

        async/await的方式:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static async Task ThrowNotImplementedExceptionAsync()
            {
                throw new NotImplementedException();
            }
    
            static async Task ThrowInvalidOperationExceptionAsync()
            {
                throw new InvalidOperationException();
            }
    
            static async Task Normal()
            {
                await Fun();
            }
    
            static Task Fun()
            {
                return Task.Run(() =>
                {
                    for (int i = 1; i <= 10; i++)
                    {
                        Console.WriteLine("i={0}", i);
                        Thread.Sleep(200);
                    }
                });
            }
    
            static async Task ObserveOneExceptionAsync()
            {
                var task1 = ThrowNotImplementedExceptionAsync();
                var task2 = ThrowInvalidOperationExceptionAsync();
                var task3 = Normal();
    
    
                try
                {
                    //异步的方式
                    Task allTasks = Task.WhenAll(task1, task2, task3);
                    await allTasks;
                    //同步的方式
                    //Task.WaitAll(task1, task2, task3);
                }
                catch (NotImplementedException ex)
                {
                    Console.WriteLine("task1 任务报错!");
                }
                catch (InvalidOperationException ex)
                {
                    Console.WriteLine("task2 任务报错!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("任务报错!");
                }
    
            }
    
            public static void Main()
            {
                Task task = ObserveOneExceptionAsync();
                Console.WriteLine("主线程继续运行........");
                task.Wait();
            }
        }
    }

      2.5、Task.FromResult的应用

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static IDictionary<string, string> cache = new Dictionary<string, string>()
            {
                {"0001","A"},
                {"0002","B"},
                {"0003","C"},
                {"0004","D"},
                {"0005","E"},
                {"0006","F"},
            };
    
            public static void Main()
            {
                Task<string> task = GetValueFromCache("0006");
                Console.WriteLine("主程序继续执行。。。。");
                string result = task.Result;
                Console.WriteLine("result={0}", result);
    
            }
    
            private static Task<string> GetValueFromCache(string key)
            {
                Console.WriteLine("GetValueFromCache开始执行。。。。");
                string result = string.Empty;
                //Task.Delay(5000);
                Thread.Sleep(5000);
                Console.WriteLine("GetValueFromCache继续执行。。。。");
                if (cache.TryGetValue(key, out result))
                {
                    return Task.FromResult(result);
                }
                return Task.FromResult("");
            }
    
        }
    }

      2.6、使用IProgress实现异步编程的进程通知
      IProgress<in T>只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress<in T>的实现类Progress<in T>的构造函数接收类型为Action<T>的形参,通过这个委托让进度显示在UI界面中。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void DoProcessing(IProgress<int> progress)
            {
                for (int i = 0; i <= 100; ++i)
                {
                    Thread.Sleep(100);
                    if (progress != null)
                    {
                        progress.Report(i);
                    }
                }
            }
    
            static async Task Display()
            {
                //当前线程
                var progress = new Progress<int>(percent =>
                {
                    Console.Clear();
                    Console.Write("{0}%", percent);
                });
                //线程池线程
                await Task.Run(() => DoProcessing(progress));
                Console.WriteLine("");
                Console.WriteLine("结束");
            }
    
            public static void Main()
            {
                Task task = Display();
                task.Wait();
            }
        }
    }

      2.7、Factory.FromAsync的应用 (简APM模式(委托)转换为任务)(BeginXXX和EndXXX)
      带回调方式的

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private delegate string AsynchronousTask(string threadName);
    
            private static string Test(string threadName)
            {
                Console.WriteLine("Starting...");
                Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Thread.CurrentThread.Name = threadName;
                return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
            }
    
            private static void Callback(IAsyncResult ar)
            {
                Console.WriteLine("Starting a callback...");
                Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
                Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
            }
    
            //执行的流程是 先执行Test--->Callback--->task.ContinueWith
            static void Main(string[] args)
            {
                AsynchronousTask d = Test;
                Console.WriteLine("Option 1");
                Task<string> task = Task<string>.Factory.FromAsync(
                    d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
    
                task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
                    t.Result));
    
                while (!task.IsCompleted)
                {
                    Console.WriteLine(task.Status);
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                }
                Console.WriteLine(task.Status);
    
            }
        }
    }

      不带回调方式的

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private delegate string AsynchronousTask(string threadName);
    
            private static string Test(string threadName)
            {
                Console.WriteLine("Starting...");
                Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Thread.CurrentThread.Name = threadName;
                return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
            }
    
            //执行的流程是 先执行Test--->task.ContinueWith
            static void Main(string[] args)
            {
                AsynchronousTask d = Test;
                Task<string> task = Task<string>.Factory.FromAsync(
                    d.BeginInvoke, d.EndInvoke, "AsyncTaskThread", "a delegate asynchronous call");
                task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: {0}",
                    t.Result));
                while (!task.IsCompleted)
                {
                    Console.WriteLine(task.Status);
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                }
                Console.WriteLine(task.Status);
    
            }
        }
    }

     

    展开全文
  • C#异步多线程Task的介绍和使用,从相关关键字到使用示例,详细解析Task和TaskCompletionSource的使用方法。

    一,相关关键字和运算符

    1.1 Async/Await 介绍和使用示例

    • 关键字 Async
      使用 ‘async’ 修饰符可将方法、lambda 表达式或匿名方法指定为异步。 如果对方法或表达式使用此修饰符,则其称为异步方法 。

      ‘async’ 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。

      如果 ‘async’ 关键字修改的方法不包含 ‘await’ 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 ‘await’ 语句的任何异步方法,因为该情况可能表示存在错误。

    定义异步方法:

    public async Task<string> AsyncTest()
    {
     
    }
    

    • 运算符 Await

      ‘await’ 运算符暂停对其所属的 ‘async’ 方法的求值,直到其操作数表示的异步操作完成。 异步操作完成后,如果有返回值 ‘await’ 运算符将返回操。 当 ‘await’ 运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。 ‘await’ 运算符不会阻止计算异步方法的线程。 当 ‘await’ 运算符暂停其所属的异步方法时,控件将返回到方法的调用方法。

    使用Await运算符:

    public async Task<string> AsyncTest()
    {
        // 等AsyncTest_1 执行完成
        await AsyncTest_1();
    }
    
    public async Task<string> AsyncTest_1()
    {
        return "任务AsyncTest_1 执行完成";
    }
    

    1.2 Async/Await 异步编程中的最佳做法

    此部分(1.3)取自 --> MSDN


    二, Task 类

    2.1 Task定义

    Task类表示不返回值并且通常以异步方式执行的单个操作。 Task 对象是在 .NET Framework 4 中首次引入的 基于任务的异步模式 的中心组件之一。 由于对象执行的工作 Task 通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行,因此可以使用 Status 属性以及 IsCanceled 、 IsCompleted 和 IsFaulted 属性来确定任务的状态。 通常,lambda 表达式用于指定任务要执行的工作。

    2.2 属性方法

    属性列表

    属性名说明
    AsyncState获取在创建 Task 时提供的状态对象,如果未提供,则为 null。(只读)
    CompletedTask此属性将返回其 Status 属性设置为的任务 RanToCompletion 。 若要创建一个返回值并运行到完成的任务,请调用 FromResult 方法。(只读)
    CreationOptions获取用于创建此任务的 TaskCreationOptions。(只读)
    CurrentId返回当前正在执行 Task 的 ID。(只读)
    Exception获取导致 AggregateException 提前结束的 Task。 如果 Task 成功完成或尚未引发任何异常,这将返回 null。(只读)
    Factory一个工厂对象,可创建多种 TaskTask<TResult> 对象。提供对用于创建和配置 Task 和 Task 实例的工厂方法的访问。
    Id任务 Id 按需分配,不一定表示任务实例的创建顺序,有可能存在冲突。若要从任务正在执行的代码内获取当前正在执行的任务的任务 ID,请使用 CurrentId 属性。
    IsCanceled如果任务由于被取消而完成,则为 true;否则为 false。
    IsCompletedtrue 如果任务已完成 (即,任务处于以下三个最终状态之一: RanToCompletionFaultedCanceled) ,则为; 否则为 false 。
    IsCompletedSuccessfullytrue 如果任务运行到完成,则为;否则为 false 。
    IsFaulted如果任务引发了未经处理的异常,则为 true;否则为 false。
    Status此任务实例的当前 TaskStatus

    方法列表

    方法名方法说明
    ConfigureAwait(bool)参数:尝试将延续任务封送回上下文,则为 true;否则为 false。 返回值:尝试将延续任务封送回原始上下文,则为 true;否则为 false。
    ContinueWith()创建一个在目标 Task 完成时异步执行的延续任务。即完成一个任务开启下一个任务。
    Delay()创建将在时间延迟后完成的任务。在完成返回的任务前要等待的参数毫秒数;如果无限期等待,则为 -1。
    Dispose()释放 Task 类的当前实例所使用的所有资源。
    FromCanceled()创建 Task 或者 Task<TResult>,它因指定的取消标记进行的取消操作而完成。
    FromException()创建 Task 或者 Task<TResult>,它在完成后出现指定的异常。
    FromResult(TResult)类型参数TResult 任务返回的结果的类型。参数 TResult 存储入已完成任务的结果。返回值: Task<TResult>已成功完成的任务。
    GetAwaiter()获取用于等待此 Taskawaiter。返回:一个 awaiter 实例。
    Run()将在线程池上运行的指定工作排队,并返回该工作的任务或 Task<TResult> 句柄。
    RunSynchronously()对当前的 Task 同步运行 TaskScheduler
    Start()启动 Task,并将它安排到当前的 TaskScheduler 中执行。
    Wait()等待当前 ‘Task’ 完成执行过程。
    WaitAll()等待所有提供的 Task 对象完成执行过程。
    WaitAny()等待提供的任一 Task 对象完成执行过程。
    WhenAll()所有提供的任务已完成时,创建将完成的任务。
    WhenAny()任何提供的任务已完成时,创建将完成的任务。
    Yield()创建异步产生当前上下文的等待任务。可以 await Task.Yield(); 在异步方法中使用来强制异步完成方法。

    1.3 Task使用

    Task最厉害的地方就是他的任务控制了,你可以很好的控制task的执行顺序,让多个task有序的工作。

    任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

    一个最简单的示例: 开启任务1 --> 调用并等待任务2完成 --> 继续执行任务1

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace TaskTest
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                Task<string> result = task_1();
    
                Console.WriteLine(result.Result);
    
                Console.ReadKey();
            }
    
            static async Task<string> task_1()
            {
                Console.WriteLine("任务1 开始执行");
    
                Console.WriteLine("等待任务2 执行完成... ");
    
                string res = await task_2();
    
                Console.WriteLine("任务2 执行完成... 返回值: " + res);
    
                return "任务1执行完成返回值";
            }
    
            static async Task<string> task_2()
            {
                Thread.Sleep(1000);
                return "任务2执行完成";
            }
    
        }
    }
    

    执行结果:
    执行结果


    三,TaskCompletionSource 类

    3.1 概念定义

    TaskCompletionSource :表示未绑定到委托的 Task 的制造者方,并通过 Task 属性提供对使用者方的访问。

    通常情况下, Task 需要表示另一个异步操作。 TaskCompletionSource 提供此目的。 它允许创建可以向使用者传递的任务,而这些使用者可以使用任务的成员,就像对待任何其他成员一样。 但是,与大多数任务不同,由创建的任务的状态由 TaskCompletionSource 中的方法显式控制 TaskCompletionSource 。 这使得外部异步操作能够传播到基础 Task。 分隔还可确保使用者不能在不访问相应的的情况下转换状态 TaskCompletionSource。所有成员 TaskCompletionSource 都是线程安全的,可同时从多个线程使用。

    3.2 属性函数

    属性:

    • Task :获取由此 ‘Task’ 创建的 ‘TaskCompletionSource’。
      此属性使使用者可以访问由此 ‘Task’ 实例控制的。 ‘SetResult()’ ‘SetException(Exception)’ ‘SetException(IEnumerable)’ 此实例上的 和 ‘SetCanceled()’ 方法 (及其 Try 变体) 都将导致相关状态在此基础上转换 Task 。

    构造函数:

    函数说明
    TaskCompletionSource()创建一个 ‘TaskCompletionSource’。
    TaskCompletionSource(Object)使用指定的状态创建一个 ‘TaskCompletionSource’。
    TaskCompletionSource(Object, TaskCreationOptions)使用指定的状态和选项创建一个 ‘TaskCompletionSource’。'Task’通过此实例创建并可通过其属性访问的将 ‘Task’ 使用指定的实例化 ‘creationOptions’。
    TaskCompletionSource(TaskCreationOptions)使用指定的选项创建一个 ‘TaskCompletionSource’。

    函数:

    PS:基础 Task 已处于以下三种最终状态的其中一种:RanToCompletion、Faulted 或 Canceled。

    函数名说明
    SetCanceled()将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
    SetCanceled(CancellationToken)使用指定的标记将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
    SetException(Exception)将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
    SetException(IEnumerable)将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
    SetResult()将基础 ‘Task’ 转换为 ‘RanToCompletion’ 状态。
    TrySetCanceled()尝试将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
    TrySetCanceled(CancellationToken)尝试将基础 ‘Task’ 转换为 ‘Canceled’ 状态。
    TrySetException(Exception)尝试将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
    TrySetException(IEnumerable)尝试将基础 ‘Task’ 转换为 ‘Faulted’ 状态。
    TrySetResult()尝试将基础 ‘Task’ 转换为 ‘RanToCompletion’ 状态。

    3.3 模拟情景

    用户操作:

    • 用户点击商品 --> 选择支付平台 --> 等待选择完成 --> 执行支付逻辑

    代码逻辑:

    • 调用支付任务 --> 等待用户选择 --> 返回用户所选 --> 执行支付逻辑
    using System;
    using System.Threading.Tasks;
    
    namespace VSProject
    {
        class Program
        {
            static TaskCompletionSource<string> ts;
    
            static void Main(string[] args)
            {
                // 开启异步任务
                TaskTest_1();
    
                while (true)
                {
                    ConsoleKeyInfo info = Console.ReadKey(true);
                    switch (info.Key)
                    {
                        case ConsoleKey.S:
                            Console.WriteLine("用户输入S键,模拟任务成功");
                            Success();
                            break;
                        case ConsoleKey.F:
                            Console.WriteLine("用户输入F键,模拟任务成功");
                            Fail();
                            break;
                    }
                }
    
                Console.ReadLine();
            }
    
            /// <summary>
            /// 异步任务
            /// </summary>
            /// <returns></returns>
            static async Task TaskTest_1()
            {
    
                Console.WriteLine("TaskTest_1 任务开始...");
                ts = new TaskCompletionSource<string>();
    
                string res;
                try
                {
                    res = await ts.Task;
                }
                catch (Exception e)
                {
                    Console.WriteLine("等待任务异常...");
                    throw e;
                }
    
                Console.WriteLine("TaskTest_1 任务结束... 传值为:" + res);
            }
    
            /// <summary>
            /// 任务成功
            /// </summary>
            static void Success()
            {
                if (ts.Task.IsCompleted) return;
    
                ts.SetResult("模拟任务完成传值");           
            }
    
            /// <summary>
            /// 任务取消或失败
            /// </summary>
            static void Fail()
            {
    
                if (ts.Task.IsCompleted) return;
    
                ts.SetException(new Exception("任务取消或失败"));
            }
        }
    
    }
    

    用户按下S键,模拟成功:
    输出
    用户按下F键,模拟失败:
    shucr


    相关链接:
    MSDN - Task 类
    MSDN - TaskCompletionSource 类

    展开全文
  • Task用法详解

    千次阅读 2020-12-31 15:42:19
    Task的创建和运行,Task有如下三种方法创建 //Task的创建与运行 static void Main(string[] args) { //1.new 方式实例化一个Task,需要通过Start方法启动 Task task=new Task(()=> { Thread.Sleep(1000); ...

    一. Task的创建和运行,Task有如下三种方法创建

    //Task的创建与运行
    static void Main(string[] args)
    {
        //1.new 方式实例化一个Task,需要通过Start方法启动
        Task task=new Task(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine($"hello,task1的线程ID为:{0}",Thread.CurrentThread.ManagedThreadId);            
        });
        task.Start();
        //2.Task.Factory.StartNew(Action)创建和启动一个Task
        Task task2=Task.Factory.StartNew(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine($"hello,task2的线程ID为:{0}",Thread.CurrentThread.ManagedThreadId);
        });
        //3.Task.Run(Action action)将任务放到线程池队列中,返回并启动一个Task
        Task task3=Task.Run(()=>
        {
           Thread.Sleep(1000);
           Console.WriteLine($"hello,task3的线程ID为:{0}",Thread.CurrentThread.ManagedThreadId);
        });
        Console.WriteLine("执行主线程");
        Console.ReadKey();
    }
    

    运行结果:

    二.创建具有返回值的Task

    如果使用Task.Result获取结果时会阻塞线程,即如果task没有完成,会等到task执行完成之后再执行后面的代码

    static void Main(string[] args)
    {
        //1.使用New方法实例化一个Task,需要通过Start方法启动
        Task<string> task1=new Task<string>(()=>
        {
           return $"hello,task1的id为{Thread.CurrentThread.ManagedThreadId}"; 
        });
        task1.Start();
        //2.使用Task.Factory.StartNew()创建和启动一个Task
        Task<string> task2=Task.Factory.StartNew<string>(()=>
        {
           return $"Hello,task2的id为{Thread.CurrentThread.ManagedThreadId}"; 
        });
        //3.Task.Run(Func func)将任务放在线程池队列,返回并启动一个Task
        Task<string> task3=Task.Run<string>(()=>
        {
            return $"hello,task3的id为{Thread.CurrentThread.ManagedThreadId}";
        });
        Console.WriteLine("执行主线程");
        Console.WriteLine(task1.Result);
        Console.WriteLine(task2.Result);
        Console.ReadKey();
    }

    运行结果:

    如果阻塞线程,则需要RunSynchronously()来阻塞线程

    static void Main(string[] args)
    {
        Task task=>new Task(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("执行Task结束");
        });
        //同步执行,task会阻塞线程
        task.RunSynchronously();
        Console.WriteLine("执行主线程结束");
        Console.ReadKey();
    }

    运行结果:

    三.Task的阻塞的方法

    Wait:表示等待task执行完成,类似于Join()

     WaitAll:当存在多个Task,只有所有的Task执行完成之后再解除阻塞

    WaitAny:当存在多个Task,只要完成一个Task就解除阻塞

    static void Main(string[] args)
    {
        Task task1=new Task(()=>
        {
            Thread.Sleep(500);
            Console.WriteLine("线程1执行完成");
        });
        task1.Start();
        Task task2=new Task(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("线程2执行完成");
        });
        task2.Start();
        //阻塞线程。task1,task2都执行完毕再执行主线程
        Task.WaitAll(new Task[]{task1,task2});
        Console.WriteLine("主线程执行完毕!");
        Console.ReadKey();
    }

    运行结果:

    四:Task的延时操作

    Wait/WaitAny/WaitAll的返回值为void,实现单纯阻塞线程.如果需要在执行完成之后再执行一些特定的代码则需要使用WhenAll/WhenAny。

    task.WhenAll(Task[] tasks).ContinueWith:表示所有的task都执行完成之后再执行后续操作

    task.WhenAny(Task[] tasks).ContinueWith:表示任意一个task执行完毕之后再执行后续操作

    static void Main(string[] args)
    {
        Task task1=new Task(()=>
        {
            Thread.Sleep(500);
            Console.WriteLine("task1执行完毕!");
        });
        task1.Start();
    
        Task task2=new Task(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("task2执行完毕!");
        });
        task2.Start();
    
        //task1与task2执行完成之后执行ContinueWith的后续操作
        Task.WhenAll(task1,task2).ContinueWith((t)=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("执行后续的操作");
        });
        Console.WriteLine("主程序执行完毕!");
        Console.ReadKey();    
    }
    //下面这段代码的执行和上面是一样的,使用Task.Factory.ContinueWhenAll/ContinueWhenAny实现
    static void Main(string[]  args)
    {
        Task task1=new Task(()=>
        {
            Thread.Sleep(500);
            Console.WriteLine("线程1执行完毕!");
        });
        task1.Start();
        
        Task task2=new Task(()=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("线程2执行完毕!");
        });
        task2.Start();
    
        //通过Task.Factory实现
        Task.Factory.ContinueWhenAll(new Task[]{task1,task2},(t)=>
        {
            Thread.Sleep(1000);
            Console.WriteLine("执行后续操作");
        });
        
        Console.WriteLine("主线程执行完毕!");
        Console.ReadKey();
    }

    五:Task取消执行:CancelTokenSource,

           Task不能像Thread一样粗暴的终止掉线程,abort();然后线程置为null;但是在实际开发中是有可能粗暴的终止线程的这时候只能使用Thread替换

    static void Main(string[] args)
    {
        CancellationTokenSource source=new CancellationTokenSource();
        int idx=0;
        //开启一个task执行任务
        Task task1=new Task(()=>
        {
            while(!source.IsCancellationRequested)
            {
                Thread.Sleep(1000);
                Console.WriteLine($"第{++idx}次执行,线程执行中");
            }
        });
        task1.Start();
        //5秒后取消任务执行
        //Thread.Sleep(5000);
        //source.Cancel();
        //也可以通过source.Token.Register(Action action)注册去取消任务触发毁掉函数
        source.CancelAfter(10000);
        Console.ReadKey();
    }

    运行结果:

    六:通过Action实现异步的编程

    private void btn_Click(object sender,EventArgs e)
    {
        Task.Run(()=>
        {
            Action<int> setValue=(i)=>{txt1.Text=i.ToString();};
            for(int i=0;i<100000;i++)
            {
                txt1.Text.Invoke(setValue,i);
            } 
        });
    }

     

    展开全文
  • TaskScheduler的核心任务是提交TaskSet到集群运算并汇报结果。 为TaskSet创建和维护一个TaskSetManager, 并追踪任务的本地性及错误信息。 遇到Straggle任务会放到其他结点进行重试。 向DAGScheduler汇报执行情况, ...
  • Android Gradle Task详解

    千次阅读 2019-04-16 21:06:57
    本文介绍如何创建一个task,如何为自定义和现有的task的添加action。以及如何配置task之间的依赖。
  • C++11中std::packaged_task的使用

    千次阅读 2020-01-31 19:53:45
    C++11中的std::packaged_task是个模板类。std::packaged_task包装任何可调用目标(函数、lambda表达式、bind表达式、函数对象)以便它可以被异步调用。它的返回值或抛出的异常被存储于能通过std::future对象访问的共享...
  • 深入理解gradle中的task

    千次阅读 2021-02-15 08:56:04
    在之前的文章中,我们讲到了如何使用gradle创建一个简单的task,以及task之间怎么依赖,甚至使用了程序来创建task。在本文中,我们会更加深入的去了解一下gradle中的task
  • 一、MapTask运行机制详解以及Map任务的并行度 整个Map阶段流程大体如上图所示。简单概述:inputFile通过split被逻辑切分为多个split文件,通过Record按行读取内容给map(用户自己实现的)进行处理,数据被map...
  • Spring Cloud Task 快速入门

    千次阅读 2020-07-18 17:21:03
    文章目录Spring Cloud Task 快速入门Spring Cloud Task 简要介绍运行环境数据库环境第一个 Spring Cloud Task 应用@EnableTask 注解CommandLineRunner和ApplicationRunner测试参考 Spring Cloud Task 快速入门 ...
  • Spring Cloud Task 简单示例

    万次阅读 2018-10-12 22:07:23
    Spring Cloud Task的目标是为Spring Boot应用程序提供创建短运行期微服务的功能。在Spring Cloud Task中,我们可以灵活地动态运行任何任务,按需分配资源并在任务完成后检索结果。Tasks是Spring Cloud Data Flow中的...
  • C# Task的各种用法和详解(推荐,精)

    万次阅读 多人点赞 2019-10-25 13:42:11
    1、Task的优势  ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:  ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;  ◆ ThreadPool不支持线程执行的...
  • Task.CompletedTaskTask.Result小记

    千次阅读 2020-12-26 19:28:56
    学习中间件出现一个知识,如下写法,一开始不太懂 中间件三部曲: 定义接口... public Task Do() { //逻辑代码 return Task.CompletedTask; } public Task DoString() { //逻辑代码 return Task.FromResult("aaa"); }
  • C#中的Task介绍

    热门讨论 2012-08-27 10:17:33
    通过简单的代码实现,介绍了.NET4.0中的Task类的使用,比较了Task和Thread的区别。适合新手学习用。
  • c# Thread、ThreadPool、Task有什么区别,什么时候用,以及Task的使用 这三者都是为了处理耗时任务,且都是异步的。 Thread Thread就是Thread,需要自己调度,适合长跑型的操作。 ThreadPoll ThreadPool是...
  • C#Task执行线程及其相关问题

    万次阅读 2018-06-01 18:54:38
    在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务...
  • C# Task和async/await详解

    万次阅读 多人点赞 2019-07-30 15:04:09
    C# Task和async/await详解什么是异步Task介绍1 Task创建和运行2 Task的阻塞方法(Wait/WaitAll/WaitAny)1 Thread阻塞线程的方法如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一...
  • C# task 取消

    千次阅读 2018-12-24 10:27:46
     我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。  在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的...
  • Gradle之task的使用

    万次阅读 2018-09-17 10:03:35
    在上一篇文章中(https://blog.csdn.net/weixin_38062353/article/details/82230239)做了对gradle的基本介绍,本篇文章将探索最基础的gradle构建块:project和task以及它们和API之间的映射。 一、构建块。 每一...
  • 多线程之Task(任务)

    万次阅读 多人点赞 2018-11-12 15:44:30
    一、介绍下Task 对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程, 然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task...
  • 【cpp-taskflow】源码分析

    千次阅读 2020-02-07 01:07:08
    taskflow一个写的比较好的基于task有向无环图(DAG)的并行调度的框架,之所以说写的比较好,个人觉得有几点原因: 1.是一个兼具学术研究和工业使用的项目,并非一个玩具 2.现代C++开发,风格简洁 (源码要求编译器...
  • c#之task与thread区别及其使用

    千次阅读 2020-02-10 20:43:19
    通过wait()对单个task进行等待,Task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。 4.task的回调执行  var testTask = new Task(() =>  {  Console.WriteLine("task start")...
  • C# Task 多任务 限制Task并发数量

    千次阅读 2019-09-29 04:55:48
    TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return true ; } protected override void QueueTask(Task task) { _tasks.Add(task); } public void Dispose() { _...
  • C#并行编程-Task

    千次阅读 2016-10-22 17:39:08
    C#并行编程-Task 菜鸟学习并行编程,参考《C#并行编程高级教程.PDF》,如有错误,欢迎指正。 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步...
  • Gradle基础:5:task的依赖与执行条件

    万次阅读 2018-11-21 06:58:39
    在使用maven的时候,compile/test/install的先后顺序和依赖关系都十分清晰,而在前面的例子中,可以通过gradle compile和gradle test来进行不同的任务执行,但是实际...这篇文章会继续来介绍task之间的依赖与执行条件。
  • Task运行过程分析1

    千次阅读 2016-05-14 13:29:45
    1、Task运行过程概述 在MapReduce计算框架中,一个应用程序被划分成Map和Reduce两个计算阶段,它们分别由一个或者多个Map Task和Reduce Task组成。其中,每个Map Task处理输入数据集合中的一片数据(InputSplit),...
  • Multi-task中的多任务loss平衡问题

    千次阅读 2019-08-24 23:29:18
    Multi-task中的多任务loss平衡问题GradNorm GradNorm GradNorm: Gradient Normalization for Adaptive Loss Balancing in Deep Multitask Networks multi-task的损失函数: L(t)=∑wi(t)Li(t)L(t)=\sum{w_i(t)L_i(t)}...
  • spring.task.execution.thread-name-prefix=task- 配置完后你就会发现定时任务可以并行异步执行了。 4.2 默认不支持分布式 Spring Task 并不是为分布式环境设计的,在分布式环境下,这种定时任务是不支持集群配置...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 994,093
精华内容 397,637
关键字:

task