金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > 编程 > 事件剖析,希尔排序

事件剖析,希尔排序

发布时间:2019-11-01 10:16编辑:编程浏览(130)

    任务概述

    线程(Thread)是创建并发的底层工具,因此有一定的局限性(不易得到返回值(必须通过创建共享域);异常的捕获和处理也麻烦;同时线程执行完毕后无法再次开启该线程),这些局限性会降低性能同时影响并发性的实现(不容易组合较小的并发操作实现较大的并发操作,会增加手工同步处理(加锁,发送信号)的依赖,容易出现问题)。

    线程池的(ThreadPool)QueueUserWorkItem方法很容发起一次异步的计算限制操作。但这个技术同样有着许多限制,最大的问题是没有内建的机制让你知道操作在什么时候完成,也没有机制在操作完成时获得返回值。

    Task类可以解决上述所有的问题。

    任务(Task)表示一个通过或不通过线程实现的并发操作,任务是可组合的,使用延续(continuation)可将它们串联在一起,它们可以使用线程池减少启动延迟,可使用回调方法避免多个线程同时等待I/O密集操作。

     

    本节对事件进行总结。

    引用:对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点的从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1次移动。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

    基础任务(Task)

    微软在.NET 4.0 引入任务(Task)的概念。通过System.Threading.Tasks命名空间使用任务。它是在ThreadPool的基础上进行封装的。Task默认都是使用池化线程,它们都是后台线程,这意味着主线程结束时其它任务也会随之停止。

    启动一个任务有多种方式,如以下示例:

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Console.WriteLine("主线程Id:{0}", Thread.CurrentThread.ManagedThreadId);
     6             int workerThreadsCount, completionPortThreadsCount;
     7             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
     8             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1}", workerThreadsCount, completionPortThreadsCount);
     9             //第一种:实例化方式Start启动
    10             {
    11                 Task task = new Task(() =>
    12                 {
    13                     Test("one-ok");
    14                 });
    15                 task.Start();
    16             }
    17             //第二种:通过Task类静态方法Run方式进行启动
    18             {
    19                 Task.Run(() =>
    20                 {
    21                     Test("two-ok");
    22                 });
    23             }
    24             //第三种:通过TaskFactory的StartNew方法启动
    25             {
    26                 TaskFactory taskFactory = new TaskFactory();
    27                 taskFactory.StartNew(() =>
    28                 {
    29                     Test("three-ok");
    30                 });
    31             }
    32             //第四种:.通过Task.Factory进行启动
    33             {
    34                 Task taskStarNew = Task.Factory.StartNew(() =>
    35                 {
    36                     Test("four-ok");
    37                 });
    38             }
    39             //第五种:通过Task对象的RunSynchronously方法启动(同步,由主线程执行,会卡主线程)
    40             {
    41                 Task taskRunSync = new Task(() =>
    42                 {
    43                     Console.WriteLine("线程Id:{0},执行方法:five-ok", Thread.CurrentThread.ManagedThreadId);
    44                 });
    45                 taskRunSync.RunSynchronously();
    46             }
    47             Thread.Sleep(1000);
    48             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
    49             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1}", workerThreadsCount, completionPortThreadsCount);
    50             Console.ReadKey();
    51         }
    52         static void Test(string o)
    53         {
    54             Thread.Sleep(2000);
    55             Console.WriteLine("线程Id:{0},执行方法:{1}", Thread.CurrentThread.ManagedThreadId, o);
    56         }
    57         /*
    58          * 作者:Jonins
    59          * 出处:http://www.cnblogs.com/jonins/
    60          */
    61     }
    

    执行结果:

    图片 1

    上面示例中除去使用RunSynchronously方法启动的是同步任务(由启用的线程执行任务)外,其它几种方式内部都由线程池内的工作者线程处理。

    说明

    1.事实上Task.Factory类型本身就是TaskFactory(任务工厂),而Task.Run(在.NET4.5引入,4.0版本调用的是后者)是Task.Factory.StartNew的简写法,是后者的重载版本,更灵活简单些。

    2.调用静态Run方法会自动创建Task对象并立即调用Start

    3.如Task.Run等方式启动任务并没有调用Start,因为它创建的是“热”任务,相反“冷”任务的创建是通过Task构造函数。

     

    二、事件:

                int[] sort = new int[13] { 1, 4, 89, 34, 56, 40, 59, 60, 39, 1, 40, 90, 48 };  // 输入一个数组
                int h = 1;
                int length = sort.Length;
                while (h > length / 3)
                {
                    h = 3 * h + 1;      // 1,4,13,40,121,364,1093,...
                }   // h的初始值根据数组元素多少而定
                while (h >= 1)  // 当h=1 时排序完成
                {
                    for (int i = h; i < length; i++)  // 将间隔为h的元素排序
                    {
                        for (int j = i; j >= h && sort[j] < sort[j - h]; j -= h) // 当满足这两个条件时交换 数值
                        {
                            int temp = sort[j];
                            sort[j] = sort[j - h];
                            sort[j - h] = temp;
                        }
                    }
                    h = h / 3;
                }
                for (int i = 0; i < sort.Length; i++)  // 输出
                   {
                        Console.Write(sort[i] + " ");
                   }
    

    返回值(Task<TResult>)&状态(Status)

    Task有一个泛型子类Task<TResult>,它允许任务返回一个值。调用Task.Run,传入一个Func<Tresult>代理或兼容的Lambda表达式,然后查询Result属性获得结果。如果任务没有完成,那么访问Result属性会阻塞当前线程,直至任务完成

    1     public static Task<TResult> Run<TResult>(Func<TResult> function);
    

    而任务的Status属性可用于跟踪任务的执行状态,如下所示:

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Task<int> task = Task.Run(() =>
     6             {
     7                 int total = 0;
     8                 for (int i = 0; i <= 100; i++)
     9                 {
    10                     total += i;
    11                 }
    12                 Thread.Sleep(2000);
    13                 return total;
    14             });
    15             Console.WriteLine("任务状态:{0}",task.Status);
    16             Thread.Sleep(1000);
    17             Console.WriteLine("任务状态:{0}", task.Status);
    18             int totalCount = task.Result;//如果任务没有完成,则阻塞
    19             Console.WriteLine("任务状态:{0}", task.Status);
    20             Console.WriteLine("总数为:{0}",totalCount);
    21             Console.ReadKey();
    22         }
    23     }
    

    执行如下:

     图片 2

    Reulst属性内部会调用Wait(等待);

    任务的Status属性是一个TaskStatus枚举类型:

    1  public TaskStatus Status { get; }
    

    说明如下:

    枚举值 说明
    Canceled

    任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;

    或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。

    Created 该任务已初始化,但尚未被计划。
    Faulted 由于未处理异常的原因而完成的任务。
    RanToCompletion 已完成执行的任务。
    Running 任务正在运行,尚未完成。
    WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
    WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成。
    WaitingToRun 该任务已被计划执行,但尚未开始执行。

     

    1、概念:Event:A member that enables an object or class to provide notifications;官方的解释是这样,就是说在C#中,事件是使

      备注:文字和代码有参考到书籍:算法 第四版(人民邮电出版社) 希尔排序 (162-163)

    任务集合返回值(WhenAll&WhenAny)

     Task中有非常方便的对并行运行的任务集合获取返回值的方式,比如WhenAllWhenAny

    对象或者类具备通知能力的成员。比如说手机收到短信提醒我去开会,那么手机就充当了一个具备通知能力的成员。说白了,事件

    1.WhenAll

    WhenAll:等待提供的所有 Task 对象完成执行过程(所有任务全部完成)。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             List<Task<int>> taskList = new List<Task<int>>();//声明一个任务集合
     6             TaskFactory taskFactory = new TaskFactory();
     7             for (int i = 0; i < 5; i++)
     8             {
     9                 int total = i;
    10                 Task<int> task = taskFactory.StartNew(() => Test(total));
    11                 taskList.Add(task);//将任务放进集合中
    12             }
    13             Console.WriteLine("主线程Id:{0},继续执行A.....", Thread.CurrentThread.ManagedThreadId);
    14             Task<int[]> taskReulstList = Task.WhenAll(taskList);//创建一个任务,该任务将集合中的所有 Task 对象都完成时完成
    15             for (int i = 0; i < taskReulstList.Result.Length; i++)//这里调用了Result,所以会阻塞线程,等待集合内所有任务全部完成
    16             {
    17                 Console.WriteLine("返回值:{0}", taskReulstList.Result[i]);//遍历任务集合内Task返回的值
    18             }
    19             Console.WriteLine("主线程Id:{0},继续执行B.....", Thread.CurrentThread.ManagedThreadId);
    20             Console.ReadKey();
    21         }
    22         private static int Test(int o)
    23         {
    24             Console.WriteLine("线程Id:{0},Task执行成功,参数为:{1}", Thread.CurrentThread.ManagedThreadId, o);
    25             Thread.Sleep(500 * o);
    26             return o;
    27         }
    28     }
    

    执行结果:

    图片 3

    的作用就是对象和类之间的信息传递的桥梁。

    2.WhenAny

    WhenAny:等待提供的任一 Task 对象完成执行过程(只要有一个任务完成)。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             List<Task<int>> taskList = new List<Task<int>>();//声明一个任务集合
     6             TaskFactory taskFactory = new TaskFactory();
     7             for (int i = 0; i < 5; i++)
     8             {
     9                 int total = i;
    10                 Task<int> task = taskFactory.StartNew(() => Test(total));
    11                 taskList.Add(task);//将任务放进集合中
    12             }
    13             Console.WriteLine("主线程Id:{0},继续执行A.....", Thread.CurrentThread.ManagedThreadId);
    14             Task<Task<int>> taskReulstList = Task.WhenAny(taskList);//创建一个任务,该任务将在集合中的任意 Task 对象完成时完成
    15             Console.WriteLine("返回值:{0}", taskReulstList.Result.Result);//得到任务集合内最先完成的任务的返回值
    16             Console.WriteLine("主线程Id:{0},继续执行B.....", Thread.CurrentThread.ManagedThreadId);
    17             Console.ReadKey();
    18         }
    19         private static int Test(int o)
    20         {
    21             Console.WriteLine("线程Id:{0},Task执行成功,参数为:{1}", Thread.CurrentThread.ManagedThreadId, o);
    22             Thread.Sleep(500 * o);
    23             return o;
    24         }
    25     }
    

    执行结果(这里返回值肯定会是0,因为休眠最短):

    图片 4

     

    2、原理:源于发生-响应模型:

    等待(Wait)&执行方式(TaskCreationOptions)

    事件源(event source) + 事件本身(event) => 事件的订阅者(event subscriber) + 事件处理器(event handler)           

    1.任务等待(Wait)

    调用任务的Wait方法可以阻塞任务直至任务完成,类似于线程的join。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Task task = Task.Run(() =>
     6             {
     7                 Console.WriteLine("线程执行Begin");
     8                 Thread.Sleep(2000);
     9                 Console.WriteLine("线程执行End");
    10             });
    11             Console.WriteLine("任务是否完成:{0}", task.IsCompleted);
    12             task.Wait();//阻塞,直至任务完成
    13             Console.WriteLine("任务是否完成:{0}", task.IsCompleted);
    14             Console.ReadKey();
    15         }
    16     }
    

    执行如下:

    图片 5

    注意

    线程调用Wait方法时,系统检测线程要等待的Task是否已经开始执行。如果是线程则会阻塞直到Task运行结束为止。但如果Task还没有开始执行任务,系统可能(取决于TaskScheduler)使用调用Wait的线程来执行Task,这种情况下调用Wait的线程不会阻塞,它会执行Task并立即返回。好处在于没有线程会被阻塞,所以减少了资源占用。不好的地方在于加入线程在调用Wait前已经获得了一个线程同步锁,而Task试图获取同一个锁,就会造成死锁的线程。

    (另外还有事件的订阅者和事件源之间的订阅关系subscribe relationship)

    2.任务执行方式(TaskCreationOptions)

    我们知道为了创建一个Task,需要调用构造函数并传递一个Action或Action<object>委托,如果传递的是期待一个Object的方法,还必须向Task的构造函数穿都要传给操作的实参。还可以选择向构造器传递一些TaskCreationOptions标记来控制Task的执行方式。

     TaskCreationOptions为枚举类型

    枚举值 说明
    None 默认。
    PreferFairness 尽可能公平的方式安排任务,即先进先执行。
    LongRunning 指定任务将是长时间运行的,会新建线程执行,不会使用池化线程。
    AttachedToParent 指定将任务附加到任务层次结构中的某个父级
    DenyChildAttach 任务试图和这个父任务连接将抛出一个InvalidOperationException
    HideScheduler 强迫子任务使用默认调度而非父级任务调度

    在默认情况下,Task内部是运行在池化线程上,这种线程会非常适合执行短计算密集作业。如果要执行长阻塞操作,则要避免使用池化线程。

    在池化线程上运行一个长任务问题不大,但是如果要同时运行多个长任务(特别是会阻塞的任务),则会对性能产生影响。最好使用:TaskCreationOptions.LongRunning。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             int workerThreadsCount, completionPortThreadsCount;
     6             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
     7             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1},主线程Id:{2}", workerThreadsCount, completionPortThreadsCount, Thread.CurrentThread.ManagedThreadId);
     8             Task task = Task.Factory.StartNew(() =>
     9             {
    10                 Console.WriteLine("长任务执行,线程Id:{0}", Thread.CurrentThread.ManagedThreadId);
    11                 Thread.Sleep(2000);
    12             }, TaskCreationOptions.LongRunning);
    13             Thread.Sleep(1000);
    14             ThreadPool.GetAvailableThreads(out workerThreadsCount, out completionPortThreadsCount);
    15             Console.WriteLine("剩余工作线程数:{0},剩余IO线程数{1},主线程Id:{2}", workerThreadsCount, completionPortThreadsCount, Thread.CurrentThread.ManagedThreadId);
    16             Console.ReadKey();
    17         }
    18     }
    

    执行结果如下:

    图片 6

    注意

    如果使运行I/O密集任务,则可以使用TaskCompletionSource和异步函数(asynchronous functions),通过回调(延续)实现并发性,而是不通过线程实现。

    如果使运行计算密集性任务,则可以使用一个生产者/消费者队列,控制这些任务的并发数量,避免出现线程和进程阻塞的问题。

     

    还是以手机收到短信提醒我去开会为例,事件源:手机吗,事件:收到短信,事件的订阅者:我,事件处理器:去开会,订阅关系:我订阅手机

    延续(continuation)&延续选项(TaskContinuationOptions)

    延续(continuation)会告诉任务在完成后继续执行下面的操作。延续通常由一个回调方法实现,它会在操作完成之后执行一次。给一个任务附加延续的方法有两种

    3、事件的声明:分为详细声明和简略声明:

    1.GetAwaiter

    任务的方法GetAwaiter是Framework 4.5新增加的,而C# 5.0的异步功能使用了这种方法,因此它非常重要。给一个任务附加延续如下:

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             Task<int> task = Task.Run(() =>
     6              {
     7                  int total = 0;
     8                  for (int i = 0; i <= 100; i++)
     9                  {
    10                      total += i;
    11                  }
    12                  Thread.Sleep(2000);
    13                  return total;
    14              });
    15             var awaiter = task.GetAwaiter();
    16             awaiter.OnCompleted(() =>
    17             {
    18                 int result = awaiter.GetResult();//在延续中获取Task的执行结果
    19                 Console.WriteLine(result);
    20             });
    21             Console.ReadKey();
    22         }
    23     }
    

    执行结果控制台会打印:5050。

    调用GetAwaiter会返回一个等待者(awaiter)对象,它会让先导(antecedent)任务在任务完成(或出错)之后执行一个代理。已经完成的任务也可以附加一个延续,这事延续会马上执行。

    注意

    1.等待者(awaiter)可以是任意对象,但必须包含特定的两个方法和一个Boolean类型属性。

    1   public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
    2     {
    3         public bool IsCompleted { get; }
    4         public TResult GetResult();
    5         public void OnCompleted(Action continuation);
    6     }
    

    2.先导任务出现错误,那么当延续代码调用awaiter.GetResult()时就会重新抛出异常。我们可以需要调用GetResult,而是直接访问先导任务的Result属性(task.Result)。

    GetResult的好处是,当先导任务出现错误时,异常可以直接抛出而不封装在AggregateException中。

    3.如果出现同步上下文,那么会自动捕捉它,然后延续提交到这个上下文中。在无需同步上下文的情况下通常不采用这种方法,使用ConfigureAwait代替它。它通常会使延续运行在先导任务所在的线程上,从而避免不必要的过载。

    1    var awaiter = task.ConfigureAwait(false).GetAwaiter();
    

    (1)详细声明:

    public delegate void MyDelegateEventHandler();
        public class Event
        {
            private MyDelegateEventHandler myDelegateEventHandler;
            public event MyDelegateEventHandler MyDelegate
            {
                add
                {
                    this.myDelegateEventHandler += value;
                }
                remove
                {
                    this.myDelegateEventHandler -= value;
                }
            }
        }
    

    本文由金沙国际官网发布于编程,转载请注明出处:事件剖析,希尔排序

    关键词:

上一篇:socket的解决方法,static的用法详解

下一篇:没有了