zoukankan      html  css  js  c++  java
  • 线程和同步

    线程和同步

    1 概述

      对于所有需要等待 的操作,例 如 ,因 为文件 、 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的。

    2 Parallel类

      2.1 用Parallel.For()方法循环

      Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代。迭代的顺序没有定义。

    复制代码
    1 ParallelLoopResult result = Parallel.For(0, 10, i =>
    2             {
    3                 Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
    4                 Thread.Sleep(10);
    5             });
    6             Console.WriteLine(result.IsCompleted);
    复制代码

      在For()方法中,前两个参数定义了循环的开头和结束。从输出可以看出,顺序是不能保证的。也可以提前中断Parallel.For()方法。

    复制代码
    1 ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) =>
    2             {
    3                 Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId);
    4                 Thread.Sleep(10);
    5                 if (i > 15)
    6                     pls.Break();
    7             });
    8             Console.WriteLine(result2.IsCompleted);
    9             Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
    复制代码

      2.2 用Parallel.ForEach()方法循环

      paraller.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于Foreach语句,但以异步方式遍历,这里也没有确定的遍历顺序。

    1     string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
    2             //  Parallel.ForEach(data, s => { Console.WriteLine(s); });
    3             Parallel.ForEach(data, (s, pls) => { if (s == "one") { Console.WriteLine("break......"); pls.Break(); } Console.WriteLine(s); Thread.Sleep(100); });

      2.3 通过Paraller.Invoke()调用多个方法

     Parallel.Invoke(Foo ,Bar);
    
     static void Foo() { Console.WriteLine("foo"); }
     static void Bar() { Console.WriteLine("bar"); }

    3 任务

      .NET 4 包含新的名称空间System.Threading.Task,它它 包含的类抽象出了线程功能,在后台使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。

      3.1启动任务

      要启动任务,可 以使用 TaskFactory类 或 Task类 的构造函数和 start()方 法。 Task类 的构造函数在创建任务上提供的灵活性较大.

    复制代码
    1    //using TaskFactory
    2             Task t1 = new TaskFactory().StartNew(TaskMethod);
    3             //using the task factory via task
    4             Task t2 = Task.Factory.StartNew(TaskMethod);
    5             //using task constructor
    6             Task t3 = new Task(TaskMethod);
    7             t3.Start();
    复制代码

    使用 Task类 的构造函数和 TaskFactory类 的 stamw()方法时,都可以传递TaskCreationOptions枚举中的值。 设置LongRunning选项,可 以通知任务调度器,该 任务需要较长时间执行,这样调度器更可能使用 新线。 如果该任务应关联到父任务上,而父任务取消了,则 该任务也应取消,此 时应设置 AuachToParent选 项。PerferFairness 值表示,调度器应提取出已在等待的第一个任务。  如果任务使用 子任务创建了其他工作,子
    任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应 以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness

    1   Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);

      3.2连续的任务

      通过任务,可 以指定在任务完成后,应 开始运行另一个特定任务.

    复制代码
     1  static void DoOnFirst()
     2         {
     3             Console.WriteLine("doing some task {0}",Task.CurrentId);
     4             Thread.Sleep(3000);
     5         }
     6         static void DoSecond(Task t)
     7         {
     8             Console.WriteLine("task {0} finished",t.Id);
     9             Console.WriteLine("this task id {0}",Task.CurrentId);
    10             Console.WriteLine("do some cleanup");
    11             Thread.Sleep(3000);
    12         }
    13 
    14  Task t1 = new Task(DoOnFirst);
    15             Task t2 = t1.ContinueWith(DoSecond);
    16             Task t3 = t2.ContinueWith(DoSecond);
    17             Task t4 = t3.ContinueWith(DoSecond);
    18             Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
    19             t1.Start();
    复制代码

    无论前一个任务是如何结束的,前 面 的连续任务总是在前一个任务结束时启 动 。 使用TaskContinuationOptions 枚举中的值,可 以指定,连续任务只有在起始任务成功(或失败)结束时启动。

      3.3任务层次的结构

    复制代码
     1  static void ParentAndChild()
     2         {
     3             var parent = new Task(ParentTask);
     4             parent.Start();
     5             Thread.Sleep(2000);
     6             Console.WriteLine(parent.Status);
     7             Thread.Sleep(4000);
     8             Console.WriteLine(parent.Status);
     9             Console.WriteLine();
    10         }
    11         private static void ParentTask()
    12         {
    13             Console.WriteLine("task id {0}",Task.CurrentId);
    14             var child = new Task(ChildTask);
    15             child.Start();
    16             Thread.Sleep(1000);
    17             Console.WriteLine("parent started child");
    18         }
    19 
    20         private static void ChildTask()
    21         {
    22             Console.WriteLine("child");
    23             Thread.Sleep(5000);
    24             Console.WriteLine("child finished");
    25         }
    复制代码

    如果父任务在子任务之前结束 ,父 任务的状态就显示为WaitingForChildrenToComplete.只要子任务也结束 时,父任务的状态就变成RanToCompletion。 ·

    4 取消架构

      4.1Parallel.For()方法的取消

    复制代码
     1 var cts = new CancellationTokenSource();
     2             cts.Token.Register(() => Console.WriteLine("token canceled"));
     3             new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start();
     4             try
     5             {
     6                 ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
     7                 {
     8                     Console.WriteLine("loop {0} started", x);
     9                     int sun = 0;
    10                     for (int i = 0; i < 100; i++)
    11                     {
    12                         Thread.Sleep(2);
    13                         sun += i;
    14                     }
    15                     Console.WriteLine("loop {0} finished",x);
    16                 });
    17             }
    18             catch (Exception ex)
    19             {
    20                 Console.WriteLine(ex.Message);
    21             }
    复制代码

      4.2任务的取消

       同样的取消模式也可用于任务。

    5 线程池

      如果有不同的小任务要完成,就可以事先创建许多线程 ,· 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在 需要释放资源时减少。不需要自己创建这样一个列表。 该列表由 ThreadPool类 托管。 这个类会在需要时增减池中线程的线程数,直 到最大的线程数。 池中的最大线程数是可配置的。如果有更多的作业要处理,线 程池中线程的个数也到了极限,最 新的作业就要排队,且 必须等待线程完成其任务。

    View Code

      线程池使用起来很简单,但 它有一些限制 :

    • 线程池 中 的所有线程都是后 台线程 。 如 果进程 的所有前 台线程都结束 了,所 有 的后 台线程就会停止 。 不能把入池的线程改为前台线程 。
    • 不 能给入池的线程设置优先级或名 称 。
    • 入池的线程只能用 于时间较短的任务 。 如 果线程要一直运行(如 Word的 拼写检查器线程),就应使用 Therd类创 建一个线程 

    6 Therd类

    使用Thread类可以创建和控制线程,

    1 new Thread(() => { Console.WriteLine("Running in thread"); }).Start();
    2             Console.WriteLine("this is the main thread");

      6.1给后台线程传递数据

      给线程传递一些数据可以采用2中方式,一种是使用带ParameterizdThreadStart委托参数的Thread构造函数,另一种方式是常见一个自定义的类,把线程的方法定义为实例方法。

      6.2后台任务

      只要有一个前台相称在运行,程序的进程就在运行,如果前台多个线程在运行,而Main()方法结束了,应用程序的进程直到所有前台完成其任务前都处于激活状态。默认情况下,用Thread创建的线程为前台线程,线程池中的线程为总是为后台线程。Thread类可以设置IsBackground属性设置是否为前台线程。

    View Code

      6.3线程的优先级

      线 程曲操作系统调度。 给线程指定优先级,就 可 以影响调度顺序。在Thread类中,可以设置Priority属性设置线程的优先级,Priority属性需要ThreadPriority枚举定义的一个值,定义级别有Highest,AboveNormal,Normal,BelowNormal和Lowest。

      6.4控制线程

      调用 Thread对 象的Start()方 法,可 以创建线程。 但是,在 调用Strat()方法后,新线程仍不是处于 Running状态,而 是处于 Unstarted状 态。 只要操作系统的线程调度器选择了要运行的线程,线程就会改为Running状态 。 读取Thread.ThreadState属 性,就可以获得线程的当前状态。使用 Thread.Sleep() 方法 ,会使线程处于WaitSleepJoin状态,在 经历Sleep()方法定义的时间段后 ,线程就会等待再次被唤醒。要停止另一个线程,可 以调用Thread.Abort()方 法。 调用这个方法时,会 在接到终止命令的线程中抛出一个ThreadAbortException类 型的异常。 用一个处理程序捕获这个异常,线程可 以在结束前完成一些清理工作。如 果需要等待线程的结束,就 可 以调用Thread.Join()方 法 。此方 法会停止当前线程 ,并把它设置为WaitSleepJoin状 态 ,直 到加入 的线程完成为止 。

    7线程问题

      7.1争用条件

      如果两个或多个线程访问相同的对象,或 者访问不同步的共享状态,就会出现争用条件。

      7.2死锁

      过多的锁定也会有麻烦。 在死锁中,至少有两个线程被挂起,并等待对方解除锁定。 由于两个线程都在等待对方,就 出现了死锁,线程将无限等待下去。

    8 同步

      8.1 Lock语句和线程安全

      C#为多个线程的同步提供了 自己的关键字:lock语 句 。 lock语 句是设置锁定和解除锁定的一种简单方式。

    View Code

    上面的代码,因为执行了5000次循环,有20个任务,所以输出的值应为100000,但是,事实并非如此。使用Lock修改DoWork方法

    View Code

    这样结果总是正确的。但是在一个地方使用Lock语句并不意味着,访问对象的其他线程都在等待,必须对每个访问共享状态的线程显示的使用同步功能。继续需改

    View Code

      8.2 Interlocked类

      Ihterlockcd类用 于使变量的简单语旬原子化。 i++不是线程安全的,它 的操作包括从内存中获取一个值,给该值递增 1,再 将它存储回内存。 这些操作都可能会被线程调度器打断。 Ihterlocked类提供了以线程安全的方式递增、 递减、'交换和读取值的方法。 与其他同步技术相 比,使用 Ihterlocked类 会快得多。 但是,它 只能用于简单的同步问题。

      8.3 Monitor类

      C#的lock语 句 ,由编译器解析为使用monitor类,与C#的 lock语 句相 比,Monitor 类的主要优点是:可 以添加一个等待被锁定的超时值 。 这样就不会无限期地等待被锁定.

    View Code

      8.4 Mutex类

      Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多个进程同步访问的一个类 由于系统能识别有名称的互斥,因 此可 以使用 它禁止应用程序启动两次

    复制代码
     1   static class Program
     2     {
     3         /// <summary>
     4         /// 应用程序的主入口点。
     5         /// </summary>
     6         [STAThread]
     7         static void Main()
     8         {
     9             bool createNew;
    10 
    11             Mutex m = new Mutex(false, "test", out createNew);
    12             if (!createNew)
    13             {
    14                 MessageBox.Show("程序已启动");
    15                 Application.Exit();
    16                 return;
    17             }
    18             Application.EnableVisualStyles();
    19             Application.SetCompatibleTextRenderingDefault(false);
    20             Application.Run(new Form1());
    21         }
    22     }
     
  • 相关阅读:
    out/host/linuxx86/obj/EXECUTABLES/aapt_intermediates/aapt 64 32 操作系统
    linux 查看路由器 电脑主机 端口号 占用
    linux proc进程 pid stat statm status id 目录 解析 内存使用
    linux vim 设置大全详解
    ubuntu subclipse svn no libsvnjavahl1 in java.library.path no svnjavahl1 in java.library.path no s
    win7 安装 ubuntu 双系统 详解 easybcd 工具 不能进入 ubuntu 界面
    Atitit.json xml 序列化循环引用解决方案json
    Atitit.编程语言and 自然语言的比较and 编程语言未来的发展
    Atitit.跨语言  文件夹与文件的io操作集合  草案
    Atitit.atijson 类库的新特性设计与实现 v3 q31
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5184371.html
Copyright © 2011-2022 走看看