zoukankan      html  css  js  c++  java
  • 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理

    1. 概述

      本章主要讲解.net4.5如何实现多线程和异步处理的相关内容。

    2. 主要内容

      2.1 理解线程

          ① 使用Thread类

      public static class Program 
      { 
          public static void ThreadMethod() 
          { 
              for (int i = 0; i < 10; i++) 
              { 
                  Console.WriteLine(“ThreadProc: {0}”, i); 
                  Thread.Sleep(0); 
              } 
          } 
          public static void Main() 
          { 
              Thread t new Thread(new ThreadStart(ThreadMethod)); 
              t.Start(); 
              for (int i = 0; i < 4; i++) 
              { 
                  Console.WriteLine(“Main thread: Do some work.”); 
                  Thread.Sleep(0); 
              }
              t.Join();
          }
      }

          *Thread.Join()方法的调用是通知主线程等待,直到其他线程执行完毕。

          *Thread.Sleep(0)是标记当前线程的状态为完成,这样系统就可以立即切换到其他线程了。

          ② 如果某线程的IsBackground属性设置为true,主程序退出前将不会考虑该线程是否还在执行。

          ③ 使用ParameterizedThreadStart委托,可以通过线程的start方法传递数据。

          ④ 用ThreadStatic Attribute 标记一个属性,每个使用该属性的线程都会获得一个该属性的副本。

          ⑤ 使用ThreadLocal<T>类,你可以定义线程本地数据,并且为每一个线程单独初始化。

    public static class Program
    {
        public static ThreadLocal<int> _field = 
             new TheadLocal<int>(() => 
                 {
                      return Thread.CurrentThread.ManagedThreadId;   
                 });  
    
        public static void Main()
        {
              new Thread(() =>
                 {   
                      for (int x=0; x < _field.value; x++)
                          Console.WriteLine("Thread A: {0}", x);
                 }).Start();
    
                new Thread(() =>
                 {   
                      for (int x=0; x < _field.value; x++)
                          Console.WriteLine("Thread B: {0}", x);
                 }).Start();
    
                 Console.ReadKey();
        }
    }    

          ⑥ 线程池

            提供线程的管理和重用。Web服务器接收请求,就是一个使用线程池的典型的例子。

      2.2 使用Task

          使用Task类,可以知道线程的完成状态以及接收返回值。

          ① 使用ContinueWith方法,可以实现任务完成之后的后续任务。

    Task<int> t = Task.Run(() => { return 42; });
    t.ContinueWith(() => { Console.WriteLine("Canceled");}, 
        TaskContinationOptions.OnlyOnCanceled);
    t.ContinueWith(() => { Console.WriteLine("Faulted");}, 
        TaskContinationOptions.OnlyOnFaulted);

          ② Task还可以嵌套子任务。

          ③ TaskFactory可以批量管理Task。

      2.3 使用Parallel类

        Parallel类有一对儿静态方法For、ForEach和Invoke方法,可以用这些来实现并行任务。

    Parallel.For(0, 10,  i => 
    {
        Thread.Sleep(1000);    
    });
    
    var numbers = Enumerable.Range(0, 10);
    Parallel.ForEach(numbers, i => 
    {
        Thread.Sleep(1000);
    });

        使用ParallelLoopState可以实现跳出循环(break())或者终止程序(Stop());

      2.4 使用 async 和 await

        这两个关键字用于简化异步代码逻辑。

        用async标记的方法,就具备了可以被划分指定部分到多个线程去执行的功能。具体划分哪些部分,用await关键字来标记。

    public static async Task<string> DownloadContent()
    {
        using (HttpClient client = new HttpClient())
        {
             string result = await client.GetStringAsync("http://www.z.cn");
             return result;
        }
    } 

          不需要ui交互的时候,可以调用ConfigureAwait方法禁用SynchronizationContext,可以获得更好的体验。

              * 标记了async的方法,里面要有await。

              * 标记了async的方法,不要返回void。(async事件除外)

      2.5 使用 Parallel Language Integrated Query (PLINQ)

        使用PLINQ,可以将一个顺序查询转化为并行版本。

    var numbers = Enumerable.Range(0, 100000000);
    var parallelResult = numbers.AsParallel()
        .Where(i => i % 2 == 0)
        .ToArray();

        使用AsOrdered操作,可以保证结果是有序的。

        .net framework把并行过程中的异常都集中到了AggregateException 中。

      2.6 使用并行集合(concurrent collections)

        .net framework提供了一些线程安全的集合:

        ① BlockingCollection<T> 

          删除数据时会阻塞程序,添加数据比较快。

          可以使用CompleteAdding方法通知其他被阻塞的线程。

    public static void Main()
    {
        BlockingCollection<string> col = new BlockingCollection<string>();
        Task read = Task.Run(() => 
            {
                foreach(string v in col.GetConsumingEnumerable())
                    Console.WriteLine(v);
            });
    
        Task write = Task.Run(() => 
            {
                  while(true)
                  {
                      string s = Console.ReadLine();
                      if (string.IsNullOrWhiteSpace(s))    break;
                      col.Add(s);
                  }
            });
    
            write.wait();
    }

        ② ConcurrentBag<T>

          用副本方式支持并发。无序。

          TryPeek方法在多线程中不太有用。peek过程中可能数据已经被修改了。

    ConcurrentBag<int> bag = new ConcurrentBag<int>();
    Task.Run(() => 
    {
        bag.Add(42);
        Thread.Sleep(1000);
        bag.Add(21);
    });
    Task.Run(() =>
    {
        foreach(int i in bag)
            Console.WriteLine(i);
    }).wait();

          *上面的程序仅打印42。因为向bag添加21的时候,打印程序已经执行完毕了。

        ③ ConcurrentQueue<T> 和 ConcurrentStack<T>

          也是用快照方式实现。

        ④ ConcurrentDictionary

    var dict = new ConcurrentDictionary<string, int>();
    if (dict.TryAdd("k1", 42))
        Console.WriteLine("Added");
    
    if (dict.TryUpdate("k1", 21, 42))
        Console.WriteLine("42 updated to 21");
    
    dict["k1"] = 42; //Overwhrite uncondictionally
    
    int r1 = dict.AddOrUpdate("k1", 3, (s, i) => i * 2);
    int r2 = dict.GetOrAdd("k2", 3);

    3. 总结

      ① 可以把每个线程当成一个独享cpu的程序。

      ② 建议使用ThreadPool来管理线程。

      ③ Task是对一个执行逻辑的封装。推荐用于多线程代码中。

      ④ Paralle用于代码中的并行操作。

      ⑤ PLINQ是LINQ的扩展,用于并行查询。

      ⑥ 用 async 和 await,可以用同步的形式编写异步的代码。

      ⑦ 并行集合可以线程安全的用于多线程环境中。

  • 相关阅读:
    SQL exists( select 1 from
    svn不知道这样的主机
    SVN 操作指南
    SVN导出/导入、SVN备份/还原 【小白版】
    Asp.net窄屏页面 手机端新闻列表
    装饰者模式
    适配器模式
    原型模式
    建造者模式
    抽象工厂方法
  • 原文地址:https://www.cnblogs.com/stone_lv/p/4344974.html
Copyright © 2011-2022 走看看