• 【转】【C#】【Thread】【Task】多线程


    多线程

    多线程在4.0中被简化了很多,仅仅只需要用到System.Threading.Tasks.::.Task类,下面就来详细介绍下Task类的使用。

    一、简单使用

    开启一个线程,执行循环方法,返回结果。开始线程为Start(),等待线程结束为Wait()。

            /// <summary>
            /// Task简单使用
            /// </summary>
            private void Demo1()
            {
                int i = 0;
                Random r = new Random(DateTime.Now.Second);
                Task t = new Task(() =>
                {
                    for (int v = 0; v < 100; v++)
                        i += r.Next(100);
                });
                t.Start();
                t.Wait();
                Console.WriteLine("这是执行Task1后等待完成:" + i.ToString());
                Console.ReadLine();
            }

    比以前使用Thread方便多了吧。

    上面的例子是使用外部的变量获得结果,下面的例子是用Task<T>直接返回结果,当调用Result属性时,会自动等待线程结束,等同调用了Wait()。代码如下:

            /// <summary>
            /// Task带返回值
            /// </summary>
            private void Demo2()
            {
                Random r = new Random(DateTime.Now.Second);
                Task<int> t = new Task<int>(() =>
                {
                    int i = 0;
                    for (int v = 0; v < 100; v++)
                        i += r.Next(100);
                    return i;
                });
                t.Start();
                Console.WriteLine("这是执行Task1获取返回值:" + t.Result.ToString());
                Console.ReadLine();
            }

    总结1:Task的使用比Thread简单很多,减少了同步,等待等等问题,唯一的遗憾是不支持Thread的IsBackground。

    结论1:如果不需要使用IsBackground,那么尽情的使用Task吧。经测试,Task是后台线程,也就是Isbackground=true;

    二、线程执行完毕后调用另一个线程

    也就是两个线程,有序的执行,这里使用ContinueWith(),

    t执行完毕后再执行一个task方法,不多说了代码如下:

            /// <summary>
            /// Task 执行完毕后调用另一个Task
            /// </summary>
            private void Demo3()
            {
                Random r = new Random(DateTime.Now.Second);
                Task<int> t = new Task<int>(() =>
                {
                    int i = 0;
                    for (int v = 0; v < 100; v++)
                        i += r.Next(100);
                    return i;
                });
                t.ContinueWith((Task<int> task) =>
                {
                    Console.WriteLine("这是执行完毕Task1后继续调用Task2:" + task.Result.ToString());
                });
                t.Start();
                Console.ReadLine();
            }

    也可以直接链式的写下去,代码如下:

             /// <summary>
            /// Task 执行完毕后调用另一个Task(链式写法)
            /// </summary>
            private void Demo4()
            {
                Random r = new Random(DateTime.Now.Second);
                Task<int> t = new Task<int>(() =>
                {
                    int i = 0;
                    for (int v = 0; v < 100; v++)
                        i += r.Next(100);
                    return i;
                });
                Task t2 = t.ContinueWith((Task<int> task) =>
                {
                    Console.WriteLine(task.Result.ToString());
                });
                t2.ContinueWith(task =>
                {
                    Console.WriteLine("这是执行完毕Task1后继续调用Task2,Task2后调用Task3。");
                });
                t.Start();
                Console.ReadLine();
            }

    结论2:Task可以便捷的将几个方法串行执行。

    三、带有父子关系的线程/多线程并行开启

    t带有t1,t2,t3三个子线程,执行t的时候t1,t2,t3可并行处理,t必须等待t1,t2,t3都执行完毕后才能结束。

    创建子Task时候必须指定参数为AttachedToParent。

            /// <summary>
            /// 带有父子关系的Task集合,[TaskCreationOptions.AttachedToParent]
            ///
            /// 值                说明
            /// None              默认值,此Task会被排入Local Queue中等待执行,采用LIFO模式。
            /// AttachedToParent  建立的Task必须是外围的Task的子Task,也是放入Local Queue,採LIFO模式。
            /// LongRunning       建立的Task不受Thread Pool所管理,直接新增一个Thread来执行此Task,无等待、无排程。
            /// PreferFairness    建立的Task直接放入Global Queue中,採FIFO模式。(比上面的优先级低)
            /// </summary>
            private void Demo5()
            {
                Task<int> t = new Task<int>(() =>
                {
                    Task<int> t1 = new Task<int>(() =>
                    {
                        int i = 0;
                        Random r = new Random(DateTime.Now.Second);
                        for (int v = 0; v < 100; v++)
                            i += r.Next(100);
                        return i;
                    }, TaskCreationOptions.AttachedToParent);
                    Task<int> t2 = new Task<int>(() =>
                    {
                        int i = 0;
                        Random r = new Random(DateTime.Now.Second);
                        for (int v = 0; v < 100; v++)
                            i += r.Next(100);
                        return i;
                    }, TaskCreationOptions.AttachedToParent);
                    Task<int> t3 = new Task<int>(() =>
                    {
                        int i = 0;
                        Random r = new Random(DateTime.Now.Second);
                        for (int v = 0; v < 100; v++)
                            i += r.Next(100);
                        return i;
                    }, TaskCreationOptions.AttachedToParent);
                    t1.Start();
                    t2.Start();
                    t3.Start();
                    return t1.Result + t2.Result + t3.Result;
                });
                t.Start();
                t.Wait();
                Console.WriteLine("这是带有父子关系的Task集合:" + t.Result.ToString());
                Console.ReadLine();
            }

    结论3:多个线程的同时开启在这里也很方便,也不用担心同步等问题。

    四、Task的中断

    这个很复杂,就不多说了,代码中有比较详细的介绍。

            /// <summary>
            /// 中途取消Task执行,Token
            ///
            /// 一是正常结束、二是产生例外、三是透过Cancel机制,这三种情况都会反映在Task.Status属性上
            /// 值                              说明
            /// Created                         Task已经建立,但未呼叫Start。
            /// WaitingForActivation            Task已排入排程,但尚未执行(一般我们建立的Task不会有此状态,只有ContinueWith所产生的Task才会有此状态)。
            /// WaitingToRun                    Task已排入排程,等待执行中。
            /// Running                         Task执行中。
            /// WaitingForChildrenToComplete    Task正等待子Task結束。
            /// RanToCompletion                 Task已经正常执行完毕。
            /// Canceled                        Task已被取消。
            /// Faulted                         Task执行中发生未预期例外。
            ///
            /// 除了Status属性外,Task还提供了另外三个属性来判定Task状态。
            /// 属性            说明
            /// IsCompleted     Task已经正常执行完毕。
            /// IsFaulted       Task执行中法生未预期例外。
            /// IsCanceled      Task已被取消。
            /// </summary>
            private void Demo6()
            {
                CancellationTokenSource cts = new CancellationTokenSource();
                var ctoken = cts.Token;
                Task t1 = new Task(() =>
                {
                    for (int v = 0; v < 10; v++)
                    {
                        if (ctoken.IsCancellationRequested)
                        {
                            //第一种写法
                            //这个会抛出异常
                            ctoken.ThrowIfCancellationRequested();
                            //另一种写法
                            //这个不会返回异常,但是获取不到是否是中断还是执行完毕。
                            //return;
                        }
                        Thread.Sleep(1000);
                        Console.WriteLine(v);
                    }
                }, ctoken);
                t1.Start();
                Thread.Sleep(2000);
                cts.Cancel();
                try
                {
                    t1.Wait();
                }
                catch
                {
                    if (t1.IsCanceled)
                        Console.WriteLine("cancel");
                }
                Console.ReadLine();
                cts.Dispose();
            }

    结论4:中断很复杂,但是对一般逻辑来说,是没有很大必要的。

    五、其他

    这里介绍下另一种写法Task.Factory,以及ContinueWhenAny和ContinueWhenAll两个方法。

    Task.Factory是静态工厂类,用于对Task提供一些麻烦的支持,这里主要介绍ContinueWhenAny和ContinueWhenAll。

    ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。

    ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。

    好了,还是看代码:

            /// <summary>
            /// Task.Factory
            /// ContinueWhenAny和ContinueWhenAll
            /// ContinueWhenAll所指定的函式会在传入的所有Tasks结束时执行,只会执行一次。
            /// ContinueWhenAny所指定的函式会在传入的Tasks中有任何一个结束时执行,且与ContinueWhenAll相同,只会执行一次。
            /// </summary>
            private void Demo7()
            {
                Task<int> t1 = Task.Factory.StartNew<int>(() =>
                {
                    int total = 0;
                    for (int i = 0; i < 10; i++)
                        total += i;
                    Thread.Sleep(12000);
                    return total;
                });
                Task<int> t2 = Task.Factory.StartNew<int>(() =>
                {
                    int total = 0;
                    for (int i = 10; i < 20; i++)
                        total += i;
                    Thread.Sleep(10000);
                    return total;
                });
                Task tfinal = Task.Factory.ContinueWhenAny<int>(
                             new Task<int>[] { t1, t2 }, (Task<int> task) =>
                             {
                                 if (task.Status == TaskStatus.RanToCompletion)
                                 {
                                     Console.WriteLine(task.Result);
                                 }
                             });
                Console.ReadLine();
            }

    结论5:ContinueWhenAny和ContinueWhenAll对特定条件执行,还是有些用处的。

    原文地址:http://www.cnblogs.com/sorex/archive/2010/09/18/1830130.html

  • 相关阅读:
    linux下查找文件及查找包含指定内容的文件常用命令
    小程序与h5之前切换频繁时候,点击无反应
    vue项目微信分享之后路由链接被破坏问题
    async-await用法
    小程序input组件获得焦点时placeholder内容有重影
    es6语法在ios低版本的支持性
    h5页面避免两个页面反复跳转死循环
    小程序getLocation出现的各种问题汇总
    小程序组件跳转页面存在兼容问题
    nigix反向代理
  • 原文地址:https://www.cnblogs.com/mqxs/p/5096343.html
Copyright © 2020-2023  润新知