zoukankan      html  css  js  c++  java
  • C# 并发编程 (异步编程与多线程)

    并发:同时做多件事情

    多线程:并发的一种形式,它采用多个线程来执行程序。

    并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。并行处理是多线程的一种,而多线程是并发的一种。

    异步编程:并发的一种形式,它采用 future 模式或回调(callback)机制,以避免产生不必要的 线程,异步编程的核心理念是异步操作:启动了的操作将会在一段时间后完成。这个操作 正在执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当 操作完成时,会通知它的 future,或者调用回调函数,以便让程序知道操作已经结束,

    async 和 await:这让异步编程变得几乎和同步(非并发)编程一样容易。await的作用:启动一个将会被执行的Task(该Task将会在新线程中执行),并立即返回,所以await所在的函数不会被阻塞,当Task完成后,继续执行await后面的代码。

     通常情况下,一个并发程序要使用多种技术。大多数程序至少使用了多线程(通过线程 池)和异步编程

    异步编程有两大好处。第一个好处是对于面向终端用户的 GUI 程序:异步编程提高了响应 能力。我们都遇到过在运行时会临时锁定界面的程序,异步编程可以使程序在执行任务时 仍能响应用户的输入。第二个好处是对于服务器端应用:异步编程实现了可扩展性。服务 器应用可以利用线程池满足其可扩展性,使用异步编程后,可扩展性通常可以提高一个数 量级。
    现代的异步 .NET 程序使用两个关键字:async 和 await。async 关键字加在方法声明上, 它的主要目的是使方法内的 await 关键字生效(为了保持向后兼容,同时引入了这两个关 键字)。如果 async 方法有返回值,应返回 Task<T>;如果没有返回值,应返回 Task。这些 task 类型相当于 future,用来在异步方法结束时通知主程序。

    1.Async/Await

    async 方法在开始时以同步方式执行。在 async 方法内部,await 关键字 对它的参数执行一个异步等待。它首先检查操作是否已经完成,如果完成了,就继续运行 (同步方式)。否则,它会暂停 async 方法,并返回,留下一个未完成的 task。一段时间后, 操作完成,async 方法就恢复运行。 不会阻塞UI线程 ,await方法完成自动的通知他 然后执行剩余的代码,异步是为了程序本身不卡 调用处还是异步的 调用处下面的代码还是会先执行 await实际上只是把主线程释放了,使用了其他线程代替他继续 异步不是为了让请求更快,而是为了可以处理更多请求

    异步有一个核心,是Task。而Task有一个方法,就是Wait,写法是Task.Wait()。所以,很多人把这个Wait和await混为一谈,这是错的。

    这个问题来自于Task。C#里,Task不是专为异步准备的,它表达的是一个线程,是工作在线程池里的一个线程。异步是线程的一种应用,多线程也是线程的一种应用。Wait,以及Status、IsCanceled、IsCompleted、IsFaulted等等,是给多线程准备的方法,跟异步没有半毛钱关系。当然你非要在异步中使用多线程的Wait或其它,从代码编译层面不会出错,但程序会。

    尤其,Task.Wait()是一个同步方法,用于多线程中阻塞等待。

    用Task.Wait()来实现同步方法中调用异步方法,这个用法本身就是错误的。 异步不是多线程,而且在多线程中,多个Task.Wait()使用也会死锁,也有解决和避免死锁的一整套方式。

    Task.Wait()是一个同步方法,用于多线程中阻塞等待,不是实现同步方法中调用异步方法的实现方式。

    在异步中,await表达的意思是:当前线程/方法中,await引导的方法出结果前,跳出当前线程/方法,从调用当前线程/方法的位置,去执行其它可能执行的线程/方法,并在引导的方法出结果后,把运行点拉回到当前位置继续执行;直到遇到下一个await,或线程/方法完成返回,跳回去刚才外部最后执行的位置继续执行。

    2.异步锁

    从 .NET Framework 4.5 开始,任何使用 async/await 进行修饰的方法,都会被认为是一个异步方法;实际上,这些异步方法都是基于队列的线程任务,从你开始使用 Task 去运行一段代码的时候,实际上就相当于开启了一个线程,默认情况下,这个线程数由线程池 ThreadPool 进行管理的。

    线程安全的访问方式可以通过lock来进行唯一线程限定,但如果使用await等待Task完成,则Task中不允许使用lock。

    因此采用另外一种方式完成

     

     调用

     3.ConcurrentQueue 队列

    锁的引入,带来了一定的开销和性能的损耗,并降低了程序的扩展性,而且还会有死锁的发生(虽说概率不大,但也不能不防啊),因此:使用LOCK进行并发编程显然不太适用。

    还好,微软一直在更新自己的东西:

       .NET Framework 4提供了新的线程安全和扩展的并发集合,它们能够解决潜在的死锁问题和竞争条件问题,因此在很多复杂的情形下它们能够使得并行代码更容易编写,这些集合尽可能减少使用锁的次数,从而使得在大部分情形下能够优化为最佳性能,不会产生不必要的同步开销。

    ConcurrentQueue 队列

       ConcurrentQueue 是完全无锁的,能够支持并发的添加元素,先进先出。下面贴代码,详解见注释:

    class Program
        {
            private static object o = new object();
            /*定义 Queue*/
            private static Queue<Product> _Products { get; set; }
            private static ConcurrentQueue<Product> _ConcurrenProducts { get; set; }
            /*  coder:天才卧龙  
             *  代码中 创建三个并发线程 来操作_Products 和 _ConcurrenProducts 集合,每次添加 10000 条数据 查看 一般队列Queue 和 多线程安全下的队列ConcurrentQueue 执行情况
             */
            static void Main(string[] args)
            {
                Thread.Sleep(1000);
                _Products = new Queue<Product>();
                Stopwatch swTask = new Stopwatch();//用于统计时间消耗的
                swTask.Start();
    
                /*创建任务 t1  t1 执行 数据集合添加操作*/
                Task t1 = Task.Factory.StartNew(() =>
                {
                    AddProducts();
                });
                /*创建任务 t2  t2 执行 数据集合添加操作*/
                Task t2 = Task.Factory.StartNew(() =>
                {
                    AddProducts();
                });
                /*创建任务 t3  t3 执行 数据集合添加操作*/
                Task t3 = Task.Factory.StartNew(() =>
                {
                    AddProducts();
                });
    
                Task.WaitAll(t1, t2, t3);
                swTask.Stop();
                Console.WriteLine("List<Product> 当前数据量为:" + _Products.Count);
                Console.WriteLine("List<Product> 执行时间为:" + swTask.ElapsedMilliseconds);
    
                Thread.Sleep(1000);
                _ConcurrenProducts = new ConcurrentQueue<Product>();
                Stopwatch swTask1 = new Stopwatch();
                swTask1.Start();
    
                /*创建任务 tk1  tk1 执行 数据集合添加操作*/
                Task tk1 = Task.Factory.StartNew(() =>
                {
                    AddConcurrenProducts();
                });
                /*创建任务 tk2  tk2 执行 数据集合添加操作*/
                Task tk2 = Task.Factory.StartNew(() =>
                {
                    AddConcurrenProducts();
                });
                /*创建任务 tk3  tk3 执行 数据集合添加操作*/
                Task tk3 = Task.Factory.StartNew(() =>
                {
                    AddConcurrenProducts();
                });
    
                Task.WaitAll(tk1, tk2, tk3);
                swTask1.Stop();
                Console.WriteLine("ConcurrentQueue<Product> 当前数据量为:" + _ConcurrenProducts.Count);
                Console.WriteLine("ConcurrentQueue<Product> 执行时间为:" + swTask1.ElapsedMilliseconds);
                Console.ReadLine();
            }
    
            /*执行集合数据添加操作*/
    
            /*执行集合数据添加操作*/
            static void AddProducts()
            {
                Parallel.For(0, 30000, (i) =>
                {
                    Product product = new Product();
                    product.Name = "name" + i;
                    product.Category = "Category" + i;
                    product.SellPrice = i;
                    lock (o)
                    {
                        _Products.Enqueue(product);
                    }
                });
    
            }
            /*执行集合数据添加操作*/
            static void AddConcurrenProducts()
            {
                Parallel.For(0, 30000, (i) =>
                {
                    Product product = new Product();
                    product.Name = "name" + i;
                    product.Category = "Category" + i;
                    product.SellPrice = i;
                    _ConcurrenProducts.Enqueue(product);
                });
    
            }
        }
    
        class Product
        {
            public string Name { get; set; }
            public string Category { get; set; }
            public int SellPrice { get; set; }
        }

    从执行时间上来看,使用 ConcurrentQueue 相比 LOCK 明显快了很多!

       1.BlockingCollection 与经典的阻塞队列数据结构类似,能够适用于多个任务添加和删除数据,提供阻塞和限界能力。

       2.ConcurrentBag 提供对象的线程安全的无序集合

       3.ConcurrentDictionary  提供可有多个线程同时访问的键值对的线程安全集合

       4.ConcurrentQueue   提供线程安全的先进先出集合

       5.ConcurrentStack   提供线程安全的后进先出集合

  • 相关阅读:
    转:桩模块 stub 和驱动模块 driver
    音频中PCM的概念
    pthread_exit
    为什么匿名内部类参数必须为final类型
    原创:同步与异步、阻塞与非阻塞
    (转)C语言中长度为0的数组
    编码问题
    查看Android程序使用内存
    C++中class与struct的区别(struct的类型名同时可以作为变量名)
    asn1c
  • 原文地址:https://www.cnblogs.com/netlock/p/14061160.html
Copyright © 2011-2022 走看看