• 计算限制的异步操作


    一、线程池

    二、执行上下文

    三、协作式取消和超时

    四、任务

    五、并行语言集成查询(PLINQ

    六、执行定时计算限制操作

    一、线程池

    1,CLR初始化时,线程池中是没有线程的。在内部,线程池维护了一个操作请求队列

    2,每CLR一个线程池,这个线程池有CLR控制的所有AppDomain共享。如果一个进程加载了多个CLR,那么每个CLR都有它自己的线程池
    3,要将一个异步的计算限制操作放到线程池的队列中,通常使用ThreadPool类定义的以下方法之一

            public static bool QueueUserWorkItem(WaitCallback callBack);
            public static bool QueueUserWorkItem(WaitCallback callBack, object state);

    这些方法向线程池的队列中加一个“工作项”(callBack)以及可选的状态数据。然后,所有方法会立即返回。无state参数的那个版本向回调方法传递一个null

            static void Main(string[] args)
            {
                ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5);
            }
    
            private static void ComputeBoundOp(Object state)
            {
                Console.WriteLine(state);
           //这个方法返回后,线程回到池中,等待另一个任务 }

    二、执行上下文

    1,每个线程都关联一个执行上下文数据结构。
    2,执行上下文包括
    ①安全设施(压缩栈、Thread的Principal、Windwos身份)
    ②宿主设置(参见System.Threading.HostExecutionContextManager)
    ③逻辑调用上下文数据(System.Runtime.Remoting.Messaging.CallContext的LogicalGetData和LogicalSetData方法)
    3,每当一个线程(初始线程)使用另一个线程(辅助线性)执行任务时,前者的执行上下文应该流向(复制到)辅助线性。这会对性能造成影响。
    4,System.Threading.ExecutionContext类控制线程的执行上下文如果从一个线程“流”向另一个

        public sealed class ExecutionContext : IDisposable, ISerializable
        {
            [SecurityCritical]
            public static AsyncFlowControl SuppressFlow();//阻止流
            public static void RestoreFlow();//恢复流
            public static Boolean IsFlowSuppressed();//是否阻止流
        }
            static void Main(string[] args)
            {
                //将一些数据方法Main线程的逻辑调用上下文中
                CallContext.LogicalSetData("Name", "Hunter");
    
                //执行上下文流向辅助线程,辅助线程可以访问逻辑调用上下文的数据
                ThreadPool.QueueUserWorkItem(r => Console.WriteLine(CallContext.LogicalGetData("Name")));
    
                //现在,阻止Main线程的执行上下文
                ExecutionContext.SuppressFlow();
    
                //辅助线程不能访问逻辑调用上下文的数据
                ThreadPool.QueueUserWorkItem(r => Console.WriteLine(CallContext.LogicalGetData("Name")));
    
                //恢复Main线程的执行上下文的流动,以免将来使用更多的线程池线程
                ExecutionContext.RestoreFlow();
    
                ThreadPool.QueueUserWorkItem(r => Console.WriteLine(CallContext.LogicalGetData("Name")));
    
                //输出:
                //Hunter
                //
                //Hunter
                Console.ReadLine();
            }

    三、协作式取消和超时

    1,CancellationTokenSource

            static void Main(string[] args)
            {
                //取消操作首先创建此对象
                CancellationTokenSource cts = new CancellationTokenSource();
    
                ThreadPool.QueueUserWorkItem(r => Count(cts.Token));
    
                Thread.Sleep(2000);
    
                //如果Count方法已返回,Cancel没有任何效果
                cts.Cancel();//取消操作
    
                //输出:
                //0
                //1
                //2
                //取消了
                Console.ReadLine();
            }
    
            private static void Count(CancellationToken token)
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(i);
    
                    if (token.IsCancellationRequested)//是否取消了
                    {
                        Console.WriteLine("取消了");
                        break;
                    }
                    Thread.Sleep(1000);
                }
    
            }

    要执行一个不允许被取消的操作,可向该操作传递通过调用CancellationToken.None属性而返回的CancellationToken,该属性返回的CancellationToken不和任何CancellationTokenSource对象关联(实例的私有字段为null)

     2,取消回调

            static void Main(string[] args)
            {
                //取消操作首先创建此对象
                CancellationTokenSource cts = new CancellationTokenSource();
                cts.Token.Register(() => Console.WriteLine("取消回调1"));
                cts.Token.Register(() => Console.WriteLine("取消回调2"));
    
                cts.Cancel();//取消操作
    
                //输出:
                //取消回调2
                //取消回调1
                Console.ReadLine();
            }

    ①useSynchronizationContext参数:

    false:调用Cancel的线程会顺序调用已登记的所有方法

    true:回调方法会被send给已捕捉的useSynchronizationContext对象

    ②CancellationTokenSource的Cancel方法参数

    true:抛出了未处理异常的第一个方法会阻止其他回调方法的执行,抛出的异常会从Cancel中抛出

    false:所有的回调方法都会执行。所有未处理的异常会被添加到一个集合中,抛出的异常会从Cancel中抛出(AggregateException)

    3,向一个CancellationTokenSource登记两个回调

            static void Main(string[] args)
            {
                //创建一个CancellationTokenSource
                var cts1 =new CancellationTokenSource();
                cts1.Token.Register(() => Console.WriteLine("cts1 取消了"));
    
                //创建另一个创建一个CancellationTokenSource
                var cts2 = new CancellationTokenSource();
                cts2.Token.Register(() => Console.WriteLine("cts2 取消了"));
    
                //创一个新的CancellationTokenSource,它在cts1或cts2取消时取消
                var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
                linkedCts.Token.Register(() => Console.WriteLine("linkedCts 取消了"));
    
                cts2.Cancel();
    
                Console.WriteLine(cts1.IsCancellationRequested+" "+cts2.IsCancellationRequested+" "+linkedCts.IsCancellationRequested);
    
                //输出:
                //linkedCts 取消了
                //cts2 取消了
                //Flase True True
                Console.ReadLine();
            }

    4,超时取消

            static void Main(string[] args)
            {
                
                //创建另一个创建一个CancellationTokenSource
                var cts2 = new CancellationTokenSource();
                cts2.Token.Register(() => { Console.WriteLine("cts2 取消了"); });
                cts2.CancelAfter(1000);//如果1秒钟还没有执行到cts2.Cancel()取消,则自动取消
    
                Thread.Sleep(100000);
                
                cts2.Cancel();
    
                //1秒中之后程序回输出:cts2 取消了
                Console.ReadLine();
            }

    四、任务

    ThreadPool.QueueUserWorkItem()没有内建机制让你知道操作在什么时候完成,也没有机制在完成操作时获得返回值

                ThreadPool.QueueUserWorkItem(r => { }); //调用QueueUserWorkItem
                new Task(r => { Console.WriteLine(r); }, 5).Start(); //用Task来做相同的事情。输出5
                Task.Run(() => { }); //另一个等价写法

    可选择向构造器传递一些TaskCreationOptions标志来控制Task的执行方式

            [Flags,Serializable]
            public enum TaskCreationOptions
            {
                //默认
                None = 0x0000,
                //提议TaskScheduler你希望该任务尽快执行(给你的感觉就像queue的感觉)
                PreRunning = 0x0001,
                //提议TaskScheduler应尽可能地创建线程池线程(如果是长时间运行的任务,建议使用此选项,使用此选项会创建一个线程,还不是使用线程池)
                LongRunning = 0x0002,
                //该提议总是被采纳:将一个Task和它的父Task关联
                AttachedToParent=0x0004,
                //该提议总是被采纳:如果一个任务视图和这个父任务链接,它就是一个普通任务,而不是子任务
                DenyChildAttach = 0x0008,
                //该提议总是被采纳:强迫子任务使用默认调度器而不是父任务的调度器
                HideScheduler = 0x0010,
            }

     1,等待任务完成并获取结果

            static void Main(string[] args)
            {
                //创建一个任务,现在还没有开始运行
                Task<int> t1 = new Task<int>(() => 1);
    
                //可以后再启动任务
                t1.Start();
    
                //可选择显示等待任务完成
                t1.Wait();
    
                Console.WriteLine(t1.Result);//输出:1
    
                Console.ReadLine();
            }

    任务抛出异常会被吞噬并存储到一个集合中,而线程池线程可以返回到线程池中。调用Wait方法或者Result属性时,这些成员会抛出一个System.AggregateException对象

    除了等待单个任务,还可以等待一个Task对象数组

                //会阻塞调用线程,直到数组中的任何Task对象完成
                //方法返回Int32数组索引值,指明完成的是哪个Task对象
                //方法返回后。线程被唤醒并继续运行
                //如果发生超时,方法返回-1
                //如果WaitAny通过一个CancellationToken取消,会抛出一个OperationCanceledException
                Task.WaitAny(task1, task2);
    
                //会阻塞调用线程,直到数组中的所有Task对象完成
                //所有Task对象都完成,WaitAll返回true
                //如果发生超时,方法返回false
                //如果WaitAll通过一个CancellationToken取消,会抛出一个OperationCanceledException
                Task.WaitAll(task1, task2);

    2,取消任务

            static void Main(string[] args)
            {
    
                CancellationTokenSource cts = new CancellationTokenSource();
    
                Task<Int32> t = Task.Run(() => Sum(cts.Token, 100), cts.Token);
    
                //在之后的某个时间,取消CancellationTokenSource以取消Task
                cts.Cancel();
    
                try
                {
                    //如果任务已取消,Result会抛出一个AggregateException异常
                    Console.WriteLine(t.Result);
                }
                catch (AggregateException x)
                {
                    //将任何OperationCanceledException对象都视为已处理
                    //其他任何异常都造成抛出一个新的AggregateException
                    //其中只包含未处理的异常
                    x.Handle(e => e is OperationCanceledException);
                    //所有异常都处理好之后,执行下面这一行
                    Console.WriteLine("计算已取消");
                }
    
                Console.ReadLine();
            }
    
    
            private static Int32 Sum(CancellationToken token, Int32 n)
            {
                int num = 0;
                for (; n > 0; n--)
                {
                    //如果任务取消,则抛出OperationCanceledException异常
                    //这样设计是因为Task会捕获异常
                    token.ThrowIfCancellationRequested();
    
                    checked
                    {
                        num += n;
                    }
                }
                return num;
            }

    4,任务完成时自动启动新任务 

    TaskContinuationOptions.OnlyOnCanceled//第一个任务被取消时才执行
    TaskContinuationOptions.OnlyOnFaulted//第一个任务被抛异常时才执行
    TaskContinuationOptions.OnlyOnRanToCompletion//第一个任务顺利完成时才执行

            [Flags, Serializable]
            public enum TaskContinuationOptions
            {
                //默认
                None = 0x0000,
                //提议TaskScheduler你希望该任务尽快执行
                PreferRunning = 0x0001,
                //提议TaskScheduler应尽可能地创建线程池线程
                LongRunning = 0x0002,
                //AttachedToParent:将一个Task和它的父Task关联
                AttachedToParent = 0x0004,
                //该提议总是被采纳:如果一个任务视图和这个父任务链接,它就是一个普通任务,而不是子任务
                DenyChildAttach = 0x0008,
                //该提议总是被采纳:强迫子任务使用默认调度器而不是父任务的调度器
                HideScheduler = 0x0010,
                //除非前置任务(antecedent task)完成,否则精致延续任务完成(取消)
                LazyCancellation = 0x0020,
    
                //这个标志指出你希望由执行第一个任务的线程执行ContinueWith任务。
                //第一个任务完成后,调用ContinueWith的线程接着执行ContinueWith任务
                ExecuteSynchronously = 0x80000,
    
                //这些标志指出在什么情况下运行ContinueWith任务
                NotOnRanToCompletion = 0x10000,
                NotOnFaulted = 0x20000,
                NotOnCanceled = 0x40000,
    
                //这些标志是以上三个标志的遍历组合
                OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,//第一个任务被取消时才执行
                OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,//第一个任务被抛异常时才执行
                OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled//第一个任务顺利完成时才执行
            }
            static void Main(string[] args)
            {
    
                var t1 = Task.Run(() =>Console.WriteLine("第一个任务"));
                t1.ContinueWith(task => Console.WriteLine("任务顺利完成时执行"), TaskContinuationOptions.OnlyOnRanToCompletion);
                t1.ContinueWith(task => Console.WriteLine("任务抛异常时执行"), TaskContinuationOptions.OnlyOnFaulted);
                t1.ContinueWith(task => Console.WriteLine("任务取消时执行"), TaskContinuationOptions.OnlyOnCanceled);
                //输出:
                //第一个任务
                //任务顺利完成时执行
    
                Console.ReadLine();
            }

    5,任务可以启动子任务

            static void Main(string[] args)
            {
                //提高性能,一个任务泵本需要3秒执行完,使用子任务只需要1秒多就可以执行完
                Task<Int32[]> parent = new Task<Int32[]>(() =>
                {
                    var results = new int[3];
    
                    //创建并启动3个子任务
                    new Task(() =>
                    {
                        Thread.Sleep(1000);
                        results[0] = 1;
                    }, TaskCreationOptions.AttachedToParent).Start();
                    new Task(() =>
                    {
                        Thread.Sleep(1000);
                        results[1] = 2;
                    }, TaskCreationOptions.AttachedToParent).Start();
                    new Task(() =>
                    {
                        Thread.Sleep(1000);
                        results[2] = 3;
                    }, TaskCreationOptions.AttachedToParent).Start();
    
                    return results;
                });
    
                //父任务及其子任务运行完成后,用一个延续任务显示结果
                parent.ContinueWith(parentTask => Array.ForEach(parentTask.Result, Console.WriteLine));
    
                //启动父任务,便于它启动它的子任务
                parent.Start();
    
                Console.ReadLine();
            }

     TaskCreationOptions.AttachedToParent标志将一个Task和创建它的Task关联,结果是除非所有子任务(以及子任务的子任务)结束运行,否则创建任务(父任务)不认为已经结束

    6,任务内部揭秘

    ①每个Task对象都有一组字段。必须为这些字段分配内存
    Int32 ID(只读唯一标识,首次查询此字段时分配)
    代表Task执行状态的一个Int32
    对父任务的引用
    对Task创建时指定的TaskScheduler的引用
    对回调方法的引用
    对要传给回调方法的对象的引用(可通过Task的只读AsyncState属性查询)
    对ExecutionContext的引用
    对ManualResetEventSlim对象的引用

    ②Task生存期状态

        //这些标志指出一个Task在其生命周期内的状态
        public enum TaskStatus
        {
            Created,//任务以显示创建;可以手动Start()这个任务
            WaitingForActivation,//任务以隐式创建;会自动开始
            WaitingToRun,//任务以调度,但尚未运行
            Running,//任务正在运行
            WaitingForChildrenToComplate,//任务正在等待它的子任务完成,子任务完成后它才完成
    
            //任务最重状态时以下三个之一
            RanToComplation,//运行完成
            Canceled,//取消
            Faulted//出错
        }

    首次构造Task对象时,状态为Created
    当任务启动时,它的状态变成WaitingToRun
    Task实际在一个线程上运行时,它的状态变成Runting
    任务停止运行并等待它的任何子任务时,状态变成WaitingForChildrenToComlate
    任务完成时变成以下状态之一:RanToComplate(运行完成)、Canceled(取消)、Faulted(出错)
    调用ContinueWith、ContinueWhenAll、ContinueWhenAny或FromAsync等方法创建的Task对象出于WaitingForActivation状态
    通过TaskComplationSource<TResult>对象创建的Task也出于WaitingForActivation状态

    7,任务工厂

    为什么要使用任务工厂:有时需要创建一组共享相同配置的Task对象,为避免机械地将相同的参数传给每个Task的构造器

            static void Main(string[] args)
            {
                Task parent = new Task(() =>
                {
                    var cts = new CancellationTokenSource();
                    var tf=new TaskFactory<Int32>(
                        cts.Token,//每个task都共享相同的CancellationTokenSource标记
                        TaskCreationOptions.AttachedToParent, //任务都被视为父任务的子任务
                        TaskContinuationOptions.ExecuteSynchronously, //TaskFactory创建的所有延续任务都以同步方式执行
                        TaskScheduler.Default//使用默认的任务调度器
                        );
    
                    //这个任务创建并启动3个子任务
                    var childTasks = new[]
                    {
                        tf.StartNew(() => 1),
                        tf.StartNew(() => 2),
                        tf.StartNew(() => { throw new ArgumentNullException();})
                    };
    
    
                    //任何子任务抛出异常,则终止所有子任务
                    foreach (var childTask in childTasks)
                    {
                        childTask.ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted);
                    }
    
                    //输出子任务值合计
                    tf.ContinueWhenAll(childTasks,
                        //该子任务不受 取消 影响
                        complatedTasks => complatedTasks.Where(r => !r.IsFaulted && !r.IsCanceled).Sum(r => r.Result),CancellationToken.None)
                        .ContinueWith(r => Console.WriteLine(r.Result), TaskContinuationOptions.ExecuteSynchronously);
                });
    
    
                //上个任务有异常时执行输出抛出异常的类型
                parent.ContinueWith(p =>
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (var e in p.Exception.Flatten().InnerExceptions)
                    {
                        sb.Append(e.GetType().ToString() + Environment.NewLine);
                    }
                    Console.WriteLine(sb.ToString());
    
                },TaskContinuationOptions.OnlyOnFaulted);
    
    
                parent.Start();
                Console.ReadLine();
            }

    8,任务调度器

    FCL提供了两个派生自TaskScheduler的类型:

    ①线程池任务调度器(默认使用)

    ②同步上下文任务调度器

    同步上下文任务调度器提供了图形用户界面的应用程序,例如Windows窗体等。它将所有任务都调度给应用程序的GUI线程,是所有任务代码都能成功更新UI组件(按钮、菜单项等)

            private void Form1_Load(object sender, EventArgs e)
            {
                //TaskScheduler.FromCurrentSynchronizationContext()使用同步上下文任务调度器
                Task.Run(() => { label1.Text = "同步上下文任务调度器(失败)"; }, CancellationToken.None)
                    .ContinueWith(task => { label1.Text = "同步上下文任务调度器(成功)"; }, CancellationToken.None,
                        TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
            }

    8,Parallel的静态For,ForEach,Invoke方法

    1>使用Parallel的前提条件:

    ①工作项必须能并行执行

    ②避免修改共享数据的工作项

    2>Parallel的性能开销:

    ①委托对象必须分配,而针对每个工作项都要调用一次这些委托

    ②如果为处理非常快的工作项使用Parallel的方法,则会得不偿失

            static void Main(string[] args)
            {
    
                //最后输出Complate。线程要在所有工作完成后才继续运行。如果存在异常,则抛出AggregateException
                
                //使用For执行更快
                Parallel.For(0, 100, r => Console.WriteLine(r));
    
                Parallel.ForEach(new[] {1, 2}, r => { Console.WriteLine(r); });
    
                Parallel.Invoke(()=>Method1(), () => Method2());
    
                Console.WriteLine("Complate");
                Console.ReadLine();
            }

    3>Parallel的静态For,ForEach,Invoke方法都提供了此参数

        
        public class ParallelOptions
        {
            public ParallelOptions();
    
            //允许取消操作(默认为CancellationToken.None)
            public CancellationToken CancellationToken { get; set; }
            //允许指定要使用哪个TaskScheduler(默认为TaskScheduler.Default)
            public int MaxDegreeOfParallelism { get; set; }
    
        }

     4>Parallel的静态For,ForEach方法有一些重载版本允许传递3个委托

    ①任务局部初始化委托(localInit):为参与工作的每个任务都调用一次改委托。这个委托是在任务被要求处理一个工作项之前调用的

    ②主体委托(body):为参与工作的各个线程所处理的每一项都调用一次该委托

    ③任务局部终结委托(localFinally):为参与工作的每一个任务都调用一次改委托。这个委托实在任务处理好派发给它的所有工作项之后调用的。即使主体委托代码引发一个未处理的异常,也会调用它

            static void Main(string[] args)
            {
    
                Console.WriteLine(DirectoryBytes(@"E:web", "*", SearchOption.TopDirectoryOnly));
                
                Console.ReadLine();
            }
    
            private static long DirectoryBytes(string path, string searchPattern, SearchOption searchOption)
            {
                var files = Directory.EnumerateFiles(path, searchPattern, searchOption);
                long masterTotal = 0;
                ParallelLoopResult result = Parallel.ForEach<string, long>(files,
                    () =>
                    {
                        //localInit:每个任务开始之前调用一次
                        //每个任务开始之前,总计值都初始化为0
                        return 0;
                    },
                    (file, loopState, index, taskLocalTotal) =>//taskLocalTotal接受localInit的返回值
                    {
                        //body:每个工作项调用一次
                        //获得这个文件的大小,把它添加到这个任务的累加值上
                        long fileLength = 0;
                        FileStream fs = null;
                        try
                        {
                            fs = File.OpenRead(file);
                            fileLength = fs.Length;
                        }
                        catch (IOException)
                        {
                            //忽略拒绝访问的文件
                        }
                        finally
                        {
                            if (fs != null) fs.Dispose();
                        }
    
                        return taskLocalTotal + fileLength;
                    },
                    taskLocalTotal =>//taskLocalTotal接受body的返回值
                    {
                        //localFinally:每个任务完成时调用一次
                        //将这个任务的总计值(taskLocalTotal)加到总的总计值(masterTotal)上
                        Interlocked.Add(ref masterTotal, taskLocalTotal);
                    });
                return masterTotal;
            }
        }

    loopState:

        public class ParallelLoopState
        {
            //告诉循环停止处理任何跟多的工作
            public void Stop();
            //停止之后该属性为true
            public bool IsStopped { get; }
            //告诉循环不再继续处理当前项之后的项
            public void Break();
            //该属性返回在处理过程中调用过Break方法的最低的项(从来没有调用过,则返回null)
            public Int64? LowestBreakInteration { get; }
            //任何项造成未处理的异常,则为true
            public bool IsException { get; }
            //调用过Stop、Break、取消过CancellationTokenSource,造成未处理异常。则为true
            public bool ShouldExitCurrentInteration { get; }
        }

    ParallelLoopResult:

        public struct ParallelLoopResult
        {
            //如果操作提前终止,则返回false
            public bool IsComplated { get; }
            public long? LowestBreakInteration { get; }
        }

    IsComplated=true 循环运行完成,所有项得到了处理
    IsComplated=false LowestBreakInteration=null 参与工作的某个线程调用了Stop方法
    IsComplated=false LowestBreakInteration!=null参与工作的某个线程调用了Break方法

    五、并行语言集成查询(PLINQ)

        public class Program
        {
            [Obsolete("已经过时")]
            public static void Main(string[] args)
            {
    
                //AsParallel将顺序查询转换成并行查询
                //AsSequential将并行操作转换为循序操作
                var query =
                    from type in Assembly.GetExecutingAssembly().GetExportedTypes().AsParallel().AsSequential().AsParallel()
                    from methed in type.GetMethods()
                    let obsoleteAttrType=typeof(ObsoleteAttribute)
                    where Attribute.IsDefined(methed, obsoleteAttrType)
                    orderby type.FullName
                    let obsoleteAttrObj=(ObsoleteAttribute)Attribute.GetCustomAttribute(methed,obsoleteAttrType)
                    select $"Type={type.FullName};Methed={methed.Name};Message={obsoleteAttrObj.Message}";
    
                //并行处理结果
                query.ForAll(Console.WriteLine);
                Console.ReadLine();
            }
        }

    PLINQ处理是乱序的,如果想保持顺序,则可调用ParallelEnumerable的AsOrdered方法,调用这个方法线程会组成处理数据项,然后,这些组被合并回去,同时保持顺序(会损害性能)

    PLINQ主要是划分区块,然后对区块(不同的线程)进行聚合计算,从而达到分而治之

    AsParallel():将串行的代码转换为并行

    AsOrdered():还原集合的初始顺序

    AsOrdered和OrderBy比较

    例如集合[10,1,4,2]

    AsOrdered=>[10,1,4,2]

    OrderBy=>[1,2,4,10]

    AsSequential():将并行转换为串行

    六、执行定时计算限制操作

    1,构造Timer参数:

    callback:回调方法

    state:回调方法参数

    dueTime:首次回调方法之前要等待多时毫秒(0为立即执行)

    period:以后每次回调方法之前要等待多时毫秒(Timeout.Infinite线程池只调用回调方法一次)

    使用Timer对象时,要确定一个变量在保持Timer对象的存活,否则对你的回调方法的调用就会停止

    2,演示如何让一个线程池线程立即调用回调方法

        class Program
        {
            private static Timer _timer;
            static void Main(string[] args)
            {
                //创建但不启动计时器
                _timer = new Timer(Status, null, Timeout.Infinite, Timeout.Infinite);
    
                //立即启动一次计时器
                _timer.Change(0, Timeout.Infinite);
    
                Console.ReadLine();//防止进程终止
            }
    
            private static void Status(object state)
            {
                Console.WriteLine("进入Status方法");
    
                //这个方法由一个线程池线程执行
                Thread.Sleep(1000);//模拟其它工作(1秒)
    
                //返回前让Timer在2秒后再次触发一次
                _timer.Change(2000, Timeout.Infinite);
                //这个方法返回后,线程池回归池中,等待下一个工作项
            }
        }

    利用Task的静态Delay方法和async和await关键字

        class Program
        {
            private static Timer _timer;
            static void Main(string[] args)
            {
                Status();
                Console.WriteLine("C");
                Console.ReadLine();//防止进程终止
    
                //输出:
                // A C B A B A B...
            }
    
            private static async void Status()
            {
                while (true)
                {
                    Console.WriteLine("A");
                    //在循环末尾,在不阻塞线程的前提下延迟2秒
                    await Task.Delay(2000);//await 运行线程返回
                    Console.WriteLine("B");
                    //2秒之后,某个线程会在await之后介入并继续循环
                }
            }
        }
  • 相关阅读:
    对文件上传使用表单验证
    文件上传
    自定义验证器
    WTForms常用的验证器
    Eclipse自动补全+常用快捷键
    JNI笔记
    cocos2d 2.2.6 win7下的配置
    cocos2d 3.6 win7下的配置
    python--文件删除、判断目录存在、字符串替换
    只是一个文件节点类为了项目的数据处理
  • 原文地址:https://www.cnblogs.com/zd1994/p/7450344.html
Copyright © 2020-2023  润新知