• C#高级编程笔记(17至21章节)线程/任务


    17 Visual Studio 2013

    控制台用Ctrl+F5可以显示窗口,不用加Console.ReadLine(); F5用于断点调式

    程式应该使用发布,因为发布的程序在发布时会进行优化,

    21 任务线程和同步

    21.2 Parallel类 System.Threading.Tasks

    21.2.1 Parallel.For()方法循环 返回ParallelLoopResult类型

    多次执行一个任务

    执行for循环,其中可能会并行运行迭代。(参数1,始索引(包含),参数2,结束索引(不包含),参数3)

    1、Thread.Sleep 是同步延迟,Task.Delay异步延迟。

    2、Thread.Sleep 会阻塞线程,Task.Delay不会。

    3、Thread.Sleep不能取消,Task.Delay可以。

    parallel.For()重载方法For(Int32, Int32, Action( Int32, ParallelLoopState) )

    ParallelLoopState.Break()    ParallelLoopState.stop()这二个方法后面都要加return;不然后面的语句还是会执行

    ParallelLoopState 实例方法 作用 相当于普通循环的语句
    stop() 退出循环,后面的循环体均不执行 break;
    Break() 满足相应条件的循环体不执行 continue;

    ParallelLoopResult.LowestBreakIteration属性取得最后迭代的数值

    ParallelLoopResult result =
                    Parallel.For(10, 40, (int i, ParallelLoopState pls) =>
                        {
                            Console.WriteLine("i: {0} task {1}", i, Task.CurrentId);
                            Thread.Sleep(10);
                            if (i > 15)
                                pls.Break();
                        });
     Console.WriteLine("Is completed: {0}", result.IsCompleted);
     if (!result.IsCompleted) \ 获取指示循环已完成运行,以便所有的循环迭代期间执行,并且该循环没有收到提前结束的请求。
      Console.WriteLine("lowest break iteration: {0}", result.LowestBreakIteration); //从中获取的最低迭代索引 Break 调用。

    21.2.2使用Parallel.ForEach()方法

    方法使用类似for(),主要用于迭化集合,用于迭化不确定的

    string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
    ParallelLoopResult result = Parallel.ForEach<string>(data, s =>
                  {
                    Console.WriteLine(s);
                  });

    21.2.3Parallel.Invoke()方法调用多个方法(并行运行)

    Parallel.Invoke(方法名1,方法名2);

    21.3 任务

    StartNew(Action< Object> , Object) 参数1为任务,参数2为object,   在参数1的任务中可以调用object类型,即参数2

    var tf = new TaskFactory(); //实例化TaskFactory,然后把任务模式转入其StartNew的方法启动任务
    Task t1 = tf.StartNew(TaskMethod, "using a task factory");
    
    Task t2 = Task.Factory.StartNew(TaskMethod, "factory via a task");//使用任务的Factory静态属性来访问TaskFactory调用StartNew
    
    var t3 = new Task(TaskMethod, "using a task constructor and Start");//实例化Task,使用t3.Start()来启动
    t3.Start();
    
    Task t4 = Task.Run(() => TaskMethod("using the Run method"));//net4.5使用lambda表达式

    同步任务

    var t1 = new Task(TaskMethod, "run sync");  //如果直接运行,就没有任务,也不是线程池的线程
          t1.RunSynchronously();//英语单词运行同步的意思- -

    使用单线程任务

    var t1 = new Task(TaskMethod, "long running", TaskCreationOptions.LongRunning);//指定任务是长时间运行的不会使用线程池的线程
    t1.Start();

    任务结果

    var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(8, 3));
      t1.Start();
      Console.WriteLine(t1.Result); //输出 Task<TResult> 的结果值。
      t1.Wait(); //等待 Task 完成执行过程
      Console.WriteLine("result from task: {0} {1}", t1.Result.Item1, t1.Result.Item2);

    21.3.3连续的任务

    Task t1 = new Task(DoOnFirst);
    Task t2 = t1.ContinueWith(DoOnSecond);//CoutinueWith(方法,TaskContinuationOptions.*****)重载可以在上一个任务成功或失败时执行
    t1.Start();//t1执行后运行DoOnSecond任务!后面t2可以继续添加!也可以 t1.ContinueWith(DoOnSecond);

    21.3.4 任务层次结构

    在父任务创建子任务 

    Task.Status() 获取此任务的当前状态

    21.4.1Paraller.for()方法的取消

    var cts = new CancellationTokenSource(); //一个取消标记          
          cts.Token.ThrowIfCancellationRequested();//如果已有一个取消标记,发送一个异常
          cts.Token.Register(() => Console.WriteLine("** token cancelled"));//取消时调用的委托     
          cts.CancelAfter(500);   //启动一个任务,在500毫秒后发送一个取消。   
                try
          {
            ParallelLoopResult result =
               Parallel.For(0, 100,new ParallelOptions(){CancellationToken = cts.Token},x =>
                   {
                     Console.WriteLine("loop {0} started", x);
                     int sum = 0;
                     for (int i = 0; i < 100; i++)
                     {
                       Thread.Sleep(2);
                       sum += i;
                     }
                     Console.WriteLine("loop {0} finished", x);
                   });
          }
          catch (OperationCanceledException ex)
          {
            Console.WriteLine(ex.Message);
          }

    21.4.2 任务的取消

    在这里可以理解CancellationTokenSource是一个别一个方法,一个取消事件,在执行CancellationTokenSource.Cancel()方法触发取消

    var cts = new CancellationTokenSource();
    cts.Token.Register(() => Console.WriteLine("*** task cancelled"));
    // send a cancel after 500 ms
    cts.CancelAfter(600);
    Task t1 = Task.Run(() =>
    {
        Console.WriteLine("in task");
        for (int i = 0; i < 20; i++)
        {
            Thread.Sleep(100);
            CancellationToken token = cts.Token;
            if (token.IsCancellationRequested)//这里去检测是否已触发取消
            {
                Console.WriteLine("取消请求,取消任务范围内的任务。");
                token.ThrowIfCancellationRequested();//如果取消请求抛出,如果这一句注释掉会输出"完成任务而不取消"
                break;
            }
            Console.WriteLine("in loop");
        }
        Console.WriteLine("完成任务而不取消");
    }, cts.Token);
    try
    {
        t1.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine("异常: {0}, {1}", ex.GetType().Name, ex.Message);
        foreach (var innerException in ex.InnerExceptions)
        {
            Console.WriteLine("内部异常: {0}, {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
        }
    }

    21.5 线程池

    ThreadPool.GetMaxThreads(out 线程池中辅助线程的最大数目,out 线程池中异步I/O线程的最大数目)

    ThreadPool.QueueUserWorkItem(JobForAThread);//将方法排入队列以便执行。 此方法在有线程池线程变得可用时执行。

    Thread.CurrentThread.ManagedThreadId //线程的ID

    var t1=new Thread(方法);//方法可使用lambda表达式

    21.6.1 给线程传递数据

    var d = new Data{message ="info"};//给结构体Data值
    var t2 = new Thread(方法名);//这里线程启用方法,调用方法(objcet)
    t2.Start(d);//这里启用线程,并把结构体传至方法里的objcet类启用
    var obj = new 类名();//实例化结构体
    var t1 = new Thread(类名.方法名);//类的方法内包含结构体
    t1.Start();//启动线程

    21.6.2 后台线程

    1、当在主线程中创建了一个线程,那么该线程的IsBackground默认是设置为FALSE的。

    2、当主线程退出的时候,IsBackground=FALSE的线程还会继续执行下去,直到线程执行结束。

    3、只有IsBackground=TRUE的线程才会随着主线程的退出而退出。

    4、当初始化一个线程,把Thread.IsBackground=true的时候,指示该线程为后台线程。后台线程将会随着主线程的退出而退出。

    5、原理:只要所有前台线程都终止后,CLR就会对每一个活在的后台线程调用Abort()来彻底终止应用程序。

    var t1 =new Thread(ThreadMain){Name="test1",IsBackground=false}; \false表示是前台线程
    static void ThreadMain()
    {Console.WriteLine("调进线程的名称为{0}",Thread.CurrentThread.Name) ;}

    21.6.3 线程的优先级

    ThreadA.Priority = ThreadPriority.AboveNormal;//系统优先执行优先级较高的线程,但这只意味着优先级较高的线程占有更多的CPU时间,并不意味着一定要先执行完优先级较高的线程,才会执行优先级较低的线程。这一点从运行结果中也可以看出,线程B 偶尔会出现在主线程和线程A前面。

    21.6.4 控制线程

    线程在调用start时线程状态Unstarted,线程调度器运行时Running,而当Sleep时是WaitSleepJoin状态

    Thread.ThreadState 当前线程状态 Thread.Abort()抛出异常,Thread.ResetAbort()重置继续运行线程 相关链接

    21.7.1争用条件

    就是说一个变量,在二个线程中同时判断,线程1判断完未到修改时,线程2也在判断,因还线程1还没有修改,所以判断是通过的!然后二个线程都同时去修改变量,变量之前i++的,现在就是i=i+2了! 所以为了解决这问题

    可以锁定共享对象(只有引用类型才能锁定 int strate =5 ;不能锁定strate ) lock(strate){程序},锁定放判断前,因不是引用类型能锁定,可以定义一个object变量(详细查看书本)

    21.7.2 死锁

    二个线程,线程1和2有二个锁,线程1锁了锁一后想锁二,但是锁二在线程2里锁着时,而线程2又在等待锁一,然后程序就死锁了!

    21.8同步

    原子操作:就是在执行某一操作时不被打断.相关

    互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
    同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

    21.8.1

    lock(typof(staticclass))//锁定静态成员

    lock(this)设置为线程安全,一次只有一个线程访问相同的实例

    本节主要说的是lock使用方法,使用时应该放在正确的位置!不应放在类内!相关内容请查看书本

    21.8.2 Interlocked类 System.Threading

    return Interlocked.Increment(ref state); //以原子操作的形式递增指定变量的值并存储结果。

    21.8.3Monitor类         System.Threading

    Monitor. Enter(Object) 方法获取指定对象上的排他锁。 Monitor. Exit(Object) 方法释放指定对象上的排他锁。

    21.8.4SpinLock结构 ,因为是结构,把一个变量赋予另一个变量会创建一个副本,所以是通过引用传送SPinLock

    与Monitor相似方法一样, 在有大量的锁定,且锁定的时间总是非常短

    21.8.5 WaitHandle类 (单词等待句柄的意思)

    封装等待对共享资源的独占访问的操作系统特定的对象。 这里略过!下次再看

    21.8.6 Mutex类 互斥 派生自基类WaitHandle

    if(mutax.WaitOne())//获得互斥锁
    {
          try{}//同步程序
          finally {mutax.ReleaseMutax();}//释放互斥
    }
    else
    {
          //等待时发生了一些问题
    }
    bool createdNew;
    var mutex = new Mutex(false, "SingletonWinAppMutex", out createdNew);//使用可指示调用线程是否应具有互斥体的初始所有权以及字符串是否为互斥体的名称的 Boolean 值和当线程返回时可指示调用线程是否已赋予互斥体的初始所有权的 Boolean 值初始化 Mutex 类的新实例。
    if (!createdNew)
     { }

    21.8.7Semaphore类 信号量

    信号量,如三个物理端口可用,就允许3个线程同时访问I/O端口,但第4个线程需要等待3个线程中的一个释放资源。

    可以理解为信号量主要是设置同时能使用多少个线程可以访问资源.

    SemapHoreSlim是Semaphore的轻化型版本

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Wrox.ProCSharp.Threading
    {
      class Program
      {
        static void Main()
        {
          int taskCount = 6; //6个任务数
          int semaphoreCount = 3;
          var semaphore = new SemaphoreSlim(semaphoreCount, semaphoreCount);//同时指定可同时授予的请求的初始数量和最大数量
          var tasks = new Task[taskCount];
    
          for (int i = 0; i < taskCount; i++)
          {
            tasks[i] = Task.Run(() => TaskMain(semaphore));
          }
    
          Task.WaitAll(tasks);
    
          Console.WriteLine("所有的任务完成了");
        }
    
    
        static void TaskMain(SemaphoreSlim semaphore)
        {
          bool isCompleted = false;
          while (!isCompleted)
          {
            if (semaphore.Wait(600))//阻止当前线程,直至它可进入 SemaphoreSlim 为止。,最长等待600毫秒
            {
              try
              {
                Console.WriteLine("任务 {0} 锁信号量", Task.CurrentId);
                Thread.Sleep(2000);
              }
              finally
              {
                Console.WriteLine("任务 {0} 释放信号量", Task.CurrentId);
                semaphore.Release();
                isCompleted = true;
              }
            }
            else
            {
              Console.WriteLine("超时任务 {0}; 等待 再一次",
                 Task.CurrentId);
            }
          }
        }
      }
    }

    21.8.8 Event类

    理解就是在类上声明事件,然后再调main最后等待事件触发(看书本)

    static void Main()
       {
         const int taskCount = 4;
         var mEvents = new ManualResetEventSlim[taskCount];//手动重置事件类数组
         var waitHandles = new WaitHandle[taskCount];//资源独占的操作  这里用于外调事件
         var calcs = new Calculator[taskCount];
         for (int i = 0; i < taskCount; i++)
         {
           int i1 = i;//这个变量用于计算用的测试变量
           mEvents[i] = new ManualResetEventSlim(false);//初始状态设置为非终止 
           waitHandles[i] = mEvents[i].WaitHandle;//获取ManualResetEventSlim 等于接收事件
           calcs[i] = new Calculator(mEvents[i]);//设置新的Calculator实例为的手动重置事件为非终止事件
           Task.Run(() => calcs[i1].Calculation(i1 + 1, i1 + 3));
          }
         for (int i = 0; i < taskCount; i++)
         {
           int index = WaitHandle.WaitAny(waitHandles);
           if (index == WaitHandle.WaitTimeout)
           {
             Console.WriteLine("时间结束!!");
           }
           else
           {
             mEvents[index].Reset();
             Console.WriteLine("完成任务 {0}, 结果: {1}",index, calcs[index].Result);
           }
         }
     } 
     public class Calculator
      {
        private ManualResetEventSlim mEvent; //手动重置事件
        private CountdownEvent cEvent; //表示在计数变为零时会得到信号通知的同步基元。
    
       public int Result { get; private set; }
    
        public Calculator(ManualResetEventSlim ev)
        {
          this.mEvent = ev;
        }
       public void Calculation(int x, int y)
        {
          Console.WriteLine("任务 {0} 开始计算", Task.CurrentId);
          Thread.Sleep(new Random().Next(3000));
          Result = x + y;
    
          // signal the event—completed!
          Console.WriteLine("任务 {0} 准备好了", Task.CurrentId);
          mEvent.Set();//将事件状态设置为有信号,从而允许一个或多个等待该事件的线程继续
         // cEvent.Signal();
         }
      }

    1

    21.8.9 Barrier类 相关 

    因为Main()也是参与者,所以 var barrier = new Barrier(numberTasks +1 ); 数量是3个

    barrier.SignalAndWait();//发出参与者已达到屏障并等待所有其他参与者也达到屏障。

    barrier.RemoveParticipant();//通知 Barrier,告知其将会减少一个参与者。

    21.8.10 RreaderWriterLockSlim类

    允许多个读取器,但只能有一个写入器锁定

    private static ReaderWriterLockSlim rwl = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
    rwl.EnterReadLock;     //尝试进入读取模式锁定状态。
    rwl.TryEnterWriteLock; //尝试进入写入模式锁定状态,可以选择超时时间。
    rwl.ExitReadLock();//减少读取模式的递归计数,并在生成的计数为 0(零)时退出读取模式。

    21.9 Timer 类

    private static void ThreadingTimer()
            {
                using (var t1 = new System.Threading.Timer(TimeAction, null, TimeSpan.FromSeconds(0),TimeSpan.FromSeconds(2)))//参数1方法名,参数2回调方法要使用的信息,参数3:启动延时时间(秒),参数4方法之间的间隔时间
                {
                    Thread.Sleep(15000);
                }
            }
    var t1 = new System.Timers.Timer(1000);
     t1.AutoReset = true;//触发事件多次
     t1.Elapsed += TimeAction; //间隔事件的方法
     t1.Start();
    Thread.Sleep(10000);
     t1.Stop();
     t1.Dispose();

    21.10 TPL DataFlow数据流 参考

    因暂没找到NuGet包无法做测试!!暂时跳过,

    22 安全性

    0.0学习暂停,考滤学习的重要性,后面转为SQL的学习!等学习完SQL再继续!(未完,待更新......)

  • 相关阅读:
    【YbtOJ#20068】连通子图
    【YbtOJ#20067】糖果分配
    【GMOJ6801】模拟patrick
    【GMOJ6800】模拟spongebob
    【洛谷P4449】于神之怒加强版
    【洛谷P3601】签到题
    【洛谷P2408】不同子串个数
    【洛谷P3809】【模板】后缀排序
    【JZOJ1753】锻炼身体
    【GMOJ1164】求和
  • 原文地址:https://www.cnblogs.com/praybb/p/7978042.html
Copyright © 2020-2023  润新知