第一章 管理程序流 之 实现多线程和异步处理

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,可以用同步的形式编写异步的代码。

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

时间: 2024-12-18 00:25:09

第一章 管理程序流 之 实现多线程和异步处理的相关文章

第二章 管理程序流(In .net4.5) 之 管理多线程

1. 概述 本章包括同步资源以及取消长时间任务相关的内容. 2. 主要内容 2.1 同步资源 ① lock关键字实现.会阻塞程序,有可能会导致死锁. ② volatile关键字可以禁用编译优化,用于避免优化代码时对多线程的影响. private static volatile int _flag = 0; ③ Interlocked关键字可以原子化一些简单数字运算和比较替换操作. Interlocked.Increment(ref n); // Interlocked.Decrement(ref

第五章 管理程序流(In .net4.5) 之 异常处理

1. 概述 本章包括.net4.5中异常处理相关的部分. 2. 主要内容 2.1 处理异常 ① try.cahtch.finally 机制,无需多言. ② 使用 Environment.FailFast 方法,可以立即终止程序,并写入系统事件日志.会绕过finally的执行. public static void Main() { string s = Console.ReadLine(); try { int i = int.Parse(s); if (i == 42) Environment

第三章 管理程序流(In .net4.5) 之 实现程序流

1. 概述 本章内容包括 布尔表达式.流控制方式.集合遍历 以及 流跳转. 2. 主要内容 *由于该章内容比较基础,日常用的也很多,故对一些常用的基础内容不再赘述. 2.1 使用布尔表达式 熟悉下列比较运算符:>, <, >=, <=, ==, !=. 熟悉下列逻辑表达式:&&, ||, ^. bool a = true; bool b = false; Console.WriteLine(a ^ a); //false Console.WriteLine(a ^

第四章 管理程序流(In .net4.5) 之 事件和回调

1. 概述 本章讲解如何使用 委托.lambda表达式 和 匿名方法 来创建和使用事件. 2. 主要内容 2.1 理解委托 委托是一种用方法签名形式定义的类型.可以让它指向其他方法,可以通过它调用其他方法. ① 委托支持多播(multicasting),可以用 + 和 += 操作符绑定多个方法到委托中(用 - 和 -= 取消绑定). public void Method1() {} public void Method2() {} public delegate void Del(); publ

Objective-C高级编程:iOS多线程及内存管理(第一章翻译)

写在翻译之前:当初看到这本书的时候,感觉深入浅出讲得比较到位,但是在市面上看到的翻译版本翻译的却没有原著的精髓和味道.所以产生了自己将其翻译一下给初学者一些便利的想法.所以才有了这个系列的第一章的翻译.目前剩余的部分依然在翻译过程中,估计不久之后就可以陆续地发出了. 因为本人的水平或者用词问题,本翻译难免有不周详或不正确之处.如果有人看到还望指出,我一定会尽力地修改那些不正确的部分,让更多的人可以看到更优质的资料. Chapter 1 Life before Automatic Referenc

linux及安全《Linux内核设计与实现》第一章——20135227黄晓妍

<linux内核设计与实现>第一章 第一章Linux内核简介: 1.3操作系统和内核简介 操作系统:系统包含了操作系统和所有运行在它之上的应用程序.操作系统是指整个在系统中负责完成最基本功能和系统管理的那些部分.这些部分包括内核.设备驱动程序.启动应到程序.命令行shell或者其他种类的用户界面.基本的文件管理系统工具. 内核:如果说用户界面是操作系统的外在表像,那么内核就是操作系统的内在核心. 内核空间:系统态和保护起来的内存空间. 内核的组成: 1.中断服务程序(响应中断) 2.调度程序(

第一章线程管理

Java 7 并发编程实战手册目录 代码下载(https://github.com/Wang-Jun-Chao/java-concurrency) 第一章线程管理 1.1简介 在计算机领域中,我们说的并发(Concurrency)是指一系列任务的同时运行.如果一 台电脑有多个处理器或者有一个多核处理器,这个同时性(Simultaneity)是真正意义的并 发:但是一台电脑只有一个单核处理器,这个同时性并不是真正的并发 与并发相关的另一个概念是并行(Parallelism).与并发有不同的定义一样

C++ Primer第一章(submit 转换格式)

1 ********************************************************************** 2 C++ Primer第一章 笔记 3 ********************************************************************** 4 序言(书籍推荐): 5 1.<C++标准程序库> 6 2.Scott Meryers <Effective C++> 7 3.Anothony Will

《node.js实战》第一章读后感

第一章:欢迎进入node.js世界 开始简单介绍了nodejs和前端的js一样,都使用了事件触发来完成异步请求的. 然后又简单介绍了nodejs的事件轮询机制 然后用NGINX和Apache的对比来说明异步I/O的事件轮询比有阻塞的多线程的处理能力要强 最后使用node的http模块来搭起了一个简单的服务器和一个流数据的服务器 Got It 1.node是异步单线程非阻塞I/O的JavaScript服务器开发平台. 异步:就像前端ajax一样,等触发了onreadystatechange事件再对