并发编程概述 和 并行编程(Parallel Framework)

news2025/1/13 19:58:53

任务(task)

异步编程(async&await)

并发编程概述

前言

说实话,在我软件开发的头两年几乎不考虑并发编程,请求与响应把业务逻辑尽快完成一个星期的任务能两天完成绝不拖三天(剩下时间各种浪),根本不会考虑性能问题(能接受范围内)。但随着工作内容的变化,一些问题,它的解决方案已经让我避不开并发编程这一块知识点了。为了一劳永逸,此系列与并发编程有关的系列文章诞生,希望对各有有所帮助。

基础术语

  • 同步(synchronization):关于协调线程或进程之间的活动,并确保被多个线程或进程访问的数据一直有效,同步允许线程和进程一致地操作。
  • 并发(concurrency):同时做多间事情,是关于程序的各个方面的合作和串联工作,以实现目标。
  • 进程(Process):一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位,当一个程序开始运行时,它在系统中奖开启一个或多个进程,一个进程又有多个线程组成。
  • 线程(thread):代表程序中的单个执行逻辑流程,是一个独立处理的执行路径,是轻量级的进程。
  • 多线程(multithreading):多个线程来执行程序。并发的一种形式,但不是唯一的方式。
  • 并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。多线程的一种。
  • 异步编程:并发的一种形式,采用future模式或回调(callback)机制,以避免产生不必要的线程。

异步编程

异步编程并不一定要用多线程去实现,多线程只是其中一种实现手段。在.Net中,新版funture类型有Task和Task。
老式异步编程API中采用回调或事件(event)。异步编程的核心理念是异步操作。

  • 异步操作:启动了的操作将会在一段时间后完成。这个操作执行时,不会阻塞原来的线程。启动了这个操作的线程,可以继续执行其他任务。当操作完成时,会通知它的future或调用回调函数,以便让程序指导操作已经结束。
  • 响应式编程:一种声明式的编程模式,程序在该模式中对事件做出响应,区别于异步编程是因为它是基于异步事件(asynchronous
    evnt)。并发编程的一种形式。

I/O密集与计算密集

  • I/O密集(I/O-bound):如果一个操作将大部分时间用于等待一个条件的产生,那么它就被成为I/O密集操作。
  • 计算密集(compute-bound):如果一个操作将大部分时间用于执行CPU密集操作,那么它被称为计算密集操作。

并发编程

优秀软件的关键特征就是具有并发性,程序在同一时间做着更多的事情,而不是过去我们看到的一种单请求单响应。智能化、高用户体验的程序已经离不并发编程。

并行编程(Parallel Framework)

前言

并行编程:通过编码方式利用多核或多处理器称为并行编程,多线程概念的一个子集。

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

并行编程分为如下几个结构:

1.并行的LINQ或PLINQ

2.Parallel类

3.任务并行结构

4.并发集合

5.SpinLock和SpinWait

这些是.NET 4.0引入的功能,一般被称为PFX(Parallel Framework,并行框架)。

Parallel类和任务并行结构称为TPL(Task Parallel Library,任务并行库)。

并行框架(PFX)

1.并行框架基础

当前CPU技术达到瓶颈,而制造商将关注重点转移到提高内核技术上,而标准单线程代码并不会因此而自动提高运行速度。
利用多核提升程序性能通常需要对计算密集型代码进行一些处理:
1.将代码划分成块。
2.通过多线程并行执行这些代码块。
3.结果变为可用后,以线程安全和高性能的方式整合这些结果。
传统多线程结构虽然实现功能,但难度颇高且不方便,特别是划分和整理的步骤(本质问题是:多线程同时使用相同数据时,出于线程安全考虑进行锁定的常用策略会引发大量竞争)。
而并行框架(Parallel Framework)专门用于在这些应用场景中提供帮助。

2.并行框架组成

PFX:高层由两个数据并行API组成:PLINQ或Parallel类。底层包含任务并行类和一组另外的结构为并行编程提供帮助。
在这里插入图片描述

基础并行语言集成查询(PLINQ)

语言集成查询(Language Integrated Query,LINQ)提供了一个简捷的语法来查询数据集合。而这种由一个线程顺序处理数据集合的方式我们称为顺序查询(sequential query)。

并行语言集成查询(Parallel LINQ)是LINQ的并行版。它将顺序查询转换为并行查询,在内部使用任务,将集合中数据项的处理工作分散到多个CPU上,以并发处理多个数据项。

PLINQ将自动并行化本地的LINQ查询,System.Linq.ParallelEnumerable类(它定义在System.Core.dll中,需要引用System.Linq)公开了所有标准LINQ操作符的并行版本。这些所有方法是依据System.Linq.ParallelQuery扩展而来。

1.LINQ to PLINQ
要让LINQ查询调用并行版本,必须将自己的顺序查询(基于IEnumerable或IEnumerable)转换成并行查询(基于ParallelQuery或ParallelQuery),使用ParallelEnumerable的AsParallel方法实现,如示例:
1.PLINQ执行模型
在这里插入图片描述

Parallel类

Parallel类是对线程的一个很好的抽象。该类位于System.Threading.Tasks命名空间中,提供了数据和任务并行性。

PFX通过Parallel类中的三个静态方法,提供了一种基本形式的结构化并行机制:

1.Parallel.Invoke

Parallel.Invoke:用于并行执行一组委托,示例如下:

任务并行

对于任务并行的内容,请戳 任务(Task) 和 异步编程(async&await)。

2.Parallel.For

Parallel.For:执行C# for循环的并行化等价循环,示例如下:

class ParallelDemo
    {
        static void Main(string[] args)
        {
            //顺序循环
            {
                for (int i = 0; i < 10; i++)
                {
                    Test(i);
                }
            }
            Console.WriteLine("并行化for开始");
            //顺序执行转换为并行化
            {
                Parallel.For(0, 10, i => Test(i));
            }
            //顺序执行转换为并行化(更简单的方式)
            {
                Parallel.For(0, 10, Test);
            }
            Console.ReadKey();
        }
        static void Test(int i)
        {
            Console.WriteLine($"当前线程Id:{Thread.CurrentThread.ManagedThreadId},输出结果为:{i}");
        }
    }

3.Parallel.ForEach

Parallel.ForEach:执行C# foreach循环的并行化等价循环,示例如下:

static void Main(string[] args)
        {
            顺序循环
            //{
            //    for (int i = 0; i < 10; i++)
            //    {
            //        Test(i);
            //    }
            //}
            //Console.WriteLine("并行化for开始");
            顺序执行转换为并行化
            //{
            //    Parallel.For(0, 10, i => Test(i));
            //}
            顺序执行转换为并行化(更简单的方式)
            //{
            //    Parallel.For(0, 10, Test);
            //}

            string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
            //顺序循环
            {
                foreach (string num in data)
                {
                    Test(num);
                }
            }
            Console.WriteLine("并行化foreach开始");
            //顺序执行转换为并行化
            {
                Parallel.ForEach(data, num => Test(num));
            }
            Console.ReadKey();
            Console.ReadKey();
        }
        static void Test(int i)
        {
            Console.WriteLine($"当前线程Id:{Thread.CurrentThread.ManagedThreadId},输出结果为:{i}");
        }
        static void Test(string i)
        {
            Console.WriteLine($"当前线程Id:{Thread.CurrentThread.ManagedThreadId},输出结果为:{i}");
        }

在这里插入图片描述

4.索引&跳出(ParallelLoopState)

有时迭代索引很有用处,但是切忌不可像顺序循环的用法使用共享变量(循环内i++)的方式使用,因为共享变量值在并行上下文中是线程不安全的。

同样的,因为并行For或ForEach中的循环体是一个委托,所以无法使用break语句提前退出循环,必须调用ParallelLoopState对象上的Break或Stop方法。

以ForEach为例,ForEach重载的其中之一如下,它包含Acton的其中有三个参数(TSourec=子元素,ParallelLoopState=并行循环状态,long=索引):

 public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource, ParallelLoopState, long> body)
``

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class ParallelDemo
    {
        static void Main(string[] args)
        {
            顺序循环
            //{
            //    for (int i = 0; i < 10; i++)
            //    {
            //        Test(i);
            //    }
            //}
            //Console.WriteLine("并行化for开始");
            顺序执行转换为并行化
            //{
            //    Parallel.For(0, 10, i => Test(i));
            //}
            顺序执行转换为并行化(更简单的方式)
            //{
            //    Parallel.For(0, 10, Test);
            //}

            string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
            //顺序循环
            //{
            //    foreach (string num in data)
            //    {
            //        Test(num);
            //    }
            //}
            Console.WriteLine("并行化foreach开始");
            //顺序执行转换为并行化
            {
                Parallel.ForEach(data, (num , state, i)=>
                {
                    Console.WriteLine($"当前索引为:{i},状态为:{state}");
                    Test(num);
                    if (num == "six")
                        state.Break();
                });

               
            }
            Console.ReadKey();
            Console.ReadKey();
        }
        static void Test(int i)
        {
            Console.WriteLine($"当前线程Id:{Thread.CurrentThread.ManagedThreadId},输出结果为:{i}");
        }
        static void Test(string i)
        {
            Console.WriteLine($"当前线程Id:{Thread.CurrentThread.ManagedThreadId},输出结果为:{i}");
        }
    }
}
    


并发集合概述

.NET 4.0在System.Collections.Concurrent命名空间中提供了一组新的集合。所有这些集合都完全是线程安全的:

这些集合不仅是为使用带锁的普通集合提供了快捷方式,而且可以在一般的多线程中使用并发集合,但需要注意:
1.并发集合针对并行编程进行了调整。只有在高度并发的应用场景中,传统集合的性能才能胜过它们。

2.线程安全的集合不能确保使用它的代码也是安全的。

3.如果枚举一个并发集合的同时,另一个线程要修改它,不会抛出任何异常,相反,得到旧内容与新内容的混合。

4.不存在任何List的并发版本。

5.它们的内存利用率没有非并发的Stack和Queue类高效,但对于并发访问的效果更好。

1.结构概述
这些并发集合与传统集合的区别是:它们公开了特殊方法来执行原子测试和行动操作,而这些方法都是通过IProducerConsumerCollection接口提供的。

IProducerConsumerCollection接口代表一个线程安全的生产者/消费者集合,这三个类继承并实现了IProducerConsumerCollection接口:

ConcurrentStack、ConcurrentQueue、ConcurrentBag。

它们实现的TryAdd和TryTake方法用于测试一个添加/删除操作能否执行,如果可以,则执行添加/删除操作。测试与行动不需要对传统集合上锁。

ConcurrentBag用于保存对象的无需集合,适用于调用Take或TryTake时不关心获取那个元素的额情况。

相对于并发队列或堆栈,在多线程同时调用一个ConcurrentBag的Add时,不存在竞争,但队列或堆栈并行调用Add会引起一些竞争,所以ConcurrentBag上调用Take方法非常高效。

BlockingCollection类似阻塞集合,适用于等待新元素的出现,可以把它看作一个容器,使用一个阻塞集合封装所有实现IProducerConsumerCollection的集合,并且允许从封装的集合中去除元素,若没有元素,操作会阻塞

2.基础方法
常用的一些方法,整理自 zy__ :

ConcurrentQueue:完全无锁,但面临资源竞争失败时可能会陷入自旋并重试操作。

Enqueue:在队尾插入元素

TryDequeue:尝试删除队头元素,并通过out参数返回

TryPeek:尝试将对头元素通过out参数返回,但不删除该元素。

ConcurrentStack:完全无锁,但面临资源竞争失败时可能会陷入自旋并重试操作。

Push:向栈顶插入元素

TryPop:从栈顶弹出元素,并且通过out 参数返回

TryPeek:返回栈顶元素,但不弹出。

ConcurrentBag:一个无序的集合,程序可以向其中插入元素,或删除元素。在同一个线程中向集合插入,删除元素的效率很高。

Add:向集合中插入元素

TryTake:从集合中取出元素并删除

TryPeek:从集合中取出元素,但不删除该元素。

BlockingCollection:一个支持界限和阻塞的容器

Add :向容器中插入元素

TryTake:从容器中取出元素并删除

TryPeek:从容器中取出元素,但不删除。

CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。

IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。

ConcurrentDictionary:对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁。

AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。

GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。
TryAdd:尝试在容器中添加新的键和值。

TryGetValue:尝试根据指定的键获得值。

TryRemove:尝试删除指定的键。

TryUpdate:有条件的更新当前键所对应的值。

GetEnumerator:返回一个能够遍历整个容器的枚举器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/74653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HPPH-SiO2 NPs/PEG/DSPE光克洛修饰介孔二氧化硅纳米粒子/聚乙二醇/磷脂的研究

小编这里分享的科研知识是HPPH-SiO2 NPs/PEG/DSPE光克洛修饰介孔二氧化硅纳米粒子/聚乙二醇/磷脂的研究&#xff0c;来看&#xff01; 光克洛修饰介孔二氧化硅纳米粒子的研究&#xff1a; 光敏剂的研发历经以卟吩姆钠为代表的第一代卟啉类光敏剂到以维替泊芬,他拉泊芬和替莫泊芬…

揭秘!女程序员为啥更赚钱?这4个大招,用Python做副业躺赚

关于穷&#xff0c;去年有了一个更学术的说法&#xff1a;隐形贫困人口。 就是因为有太多“种草达人”&#xff0c;让我们为了物质生活超前消费&#xff0c;再加上不理财的话&#xff0c;那简直是雪上加霜。 看到知乎上面最近有一个很火的问题&#xff1a; “90后的你&#…

为了学会更多炫酷的 canvas 效果,我熬夜复习了三角函数相关的知识点

稳定性建设之JavaScript代码不能被阻断 背景 js代码可能会因为某些原因&#xff0c;导致出错&#xff0c;进而整个后续代码有可能都被阻断。直接影响线上的稳定性 最常见的js被阻断的情况 console.log(111) // 预期 a {} // 结果 a undefined a.a 1 console.log(222) // …

web前端期末大作业 HTML游戏资讯网页设计制作 简单静态HTML网页作品 DW游戏资讯网页作业成品 游戏网站模板

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

CTO职位刚发布,一天收到100+份简历

IT行业卷可是没有想到这么卷&#xff0c;我们公司最近招聘CTO&#xff0c;一天收到100份简历&#xff0c;这里面有太多优秀的人&#xff0c;简直挑花了眼。这里面有英国毕业的&#xff0c;有北京大学的&#xff0c;还有各种985和211毕业的大学生&#xff0c;简历也是非常的丰富…

总结717

大三上半学期过去了。回想起当初那个暑假还在纠结的问题。如今&#xff0c;我也算是想通了。 回想起这样一个夜晚&#xff0c;我与好几位同学在谈论考研的事情。其中有一位同学问&#xff1a;“所以&#xff0c;你们为什么要考研呢&#xff1f;”有的同学说是“想到名校走走”…

【C++常用容器】STL基础语法学习stack容器

目录 ●stack基本概念 ●stack常用接口 ●构造函数 ●赋值操作 ●数据存取 ●大小操作 ●stack基本概念 简要介绍&#xff1a; stack是一种先进后出或后进先出的数据结构&#xff0c;它只有一个出口。栈中只有顶端元素才可以被外界使用&#xff0c;因此栈不允许有遍历行为。栈…

对vue的mixin的理解,有什么应用场景?

mixin是什么 Mixin是面向对象程序设计语言中的类&#xff0c;提供了方法的实现。 其他类可以访问mixin类的方法而不必成为其子类 当一段代码非常相似的时候就可以抽离成一个mixin mixins是一个js对象&#xff0c;它可以包含我们组件中script项中的任意功能选项&#xff0c;…

不是所有国产软件都像360一样流氓!这些良心国产软件不该被埋没

提到国产软件&#xff0c;很多人第一反应——360安全卫士。 但就像那句著名点评&#xff1a;“电脑上的常见问题有一半是安装360可以解决的&#xff0c;另一半是卸载360可以解决的。” 360可以说让人又爱又恨&#xff0c;强大的杀毒能力毋庸置疑&#xff0c;但是捆绑安装也是…

java计算机毕业设计基于安卓Android的学生个人支出管理APP

项目介绍 基于APP的学生个人支出管理系统主要针对广大学生,本设计分为用户客户端和管理员后台管理,前台用户管理使用Android Studio制作,使用了JS、HTML和uniapp开发框架,后台管理使用JAVA&#xff1a;MySQL数据库来保存数据以及上传数据。MySQL体积小、速度快,为数据的存储和…

用Python机器人监听微信群聊, 我看谁这么大的胆子敢调侃老板和前台小姐姐!

随着微信社交的兴起&#xff0c;我们加入的群也越来越多&#xff0c;一个不经意就被拉入好几个群&#xff0c;群是大家协同交流的平台&#xff0c;但是微信群却越来越泛滥&#xff0c;不知道大家有没有统计过自己浪费在毫无营养的群中的时间&#xff1f; 因为群质量太低或者群太…

Web页面测试

一、Web的功能测试 1、超链接测试 a、链接与链接的说明文字要匹配 &#xff08;注&#xff1a;也可以是图片&#xff09; b、链接的文字要描述正确 c、链接的文字要精简有效 d、链接的页面应该存在&#xff0c;不应该出现404找不到页面的错误 e、没有孤立页面&#xff0c;只有…

Android kotlin 基础知识codelab Fragment Summary

创建 fragment 在此 Codelab 中&#xff0c;您向 AndroidTrivia 应用添加了一个 fragment&#xff0c;在本课的后续两个 Codelab 中您将继续在此应用中执行操作。 fragment 是 activity 的模块化部分。fragment 有自己的生命周期&#xff0c;会接收自己的输入事件。使用 <…

宝宝经常吐奶,除了拍嗝,这6个细节也不容忽视,别因小失大

众所周知&#xff0c;宝宝吐奶是很常见的。不过&#xff0c;虽然很常见&#xff0c;但只要宝宝吐奶&#xff0c;尤其是宝宝吐奶时&#xff0c;家长们都会非常心疼&#xff0c;迫不及待地想代替宝宝承受这些不适。为什么宝宝这么容易吐奶&#xff1f;首先&#xff0c;宝宝的胃是…

JQuery 基础

JQuery对象和JS对象区别与转换 1. JQuery对象在操作时&#xff0c;更加方便。 2. JQuery对象和js对象方法不通用的. 3. 两者相互转换 * jq -- > js : jq对象[索引] 或者 jq对象.get(索引) * js -- > jq : $(js对象) <script>//1.通过…

定时执行专家 —— 模拟键盘按键、鼠标单击功能发布(可发送快捷键、热键、鼠标左键多次单击)

目录 ◆ 定时执行专家 —— 模拟键盘按键 ◆ 定时执行专家 —— 模拟鼠标单击 ◆ 定时执行专家 —— 模拟键盘按键 - 设置方法 &#xff08;1&#xff09;点击 “定时执行专家” 工具栏 “新建” 图标&#xff0c;打开 “任务对话框”&#xff08;图1&#xff09;&#xff1…

Spring中的Bean的实例化

Bean的实例化1. Bean的配置2.Bean的实例化2.1 构造器实例化2.2 静态工厂方式实例化2.3 实例工厂方式实例化1. Bean的配置 Spring 可以被看作是一个大型工厂&#xff0c;这个工厂的作用就是生产和管理 Spring 容器中的Bean。如果想要在项目中使用这个工厂&#xff0c;就需要开发…

Android开发【全面理解Activity】

一、 Activity的生命周期 和其他手机 平台 的应用 程序 一样&#xff0c;Android的应用程序 的生命周期是被统一掌控 的&#xff0c;也就是说我们写的应用程序命运掌握在别人&#xff08;系统&#xff09;的手里&#xff0c;我们不能改变它&#xff0c;只能学习并适应它。 简…

一年一度的云计算春晚——亚马逊云科技re:Invent

一年一度的云计算春晚——亚马逊云科技re:Invent&#xff0c;近日盛大开幕。那么“云计算春晚”发布了什么AI产品&#xff1f; 首先来看Amazon SageMaker&#xff0c;今年是其发布的第五年&#xff0c;各行各业已有数百万个机器学习模型使用该服务管理&#xff0c;每月进行数千…

支付服务-----功能实现逻辑

支付服务实现逻辑 简单概况一下支付服务的实现逻辑 通过支付宝的沙箱环境来模拟支付功能&#xff0c;用户点击支付宝的链接后给后端发/aliPayOrder请求&#xff0c;封装支付宝需要payVo对象&#xff0c;并且调用 String pay alipayTemplate.pay(payVo)和 return pay&#xf…