三分钟掌握共享内存 & Actor并发模型

简介: 吃点好的,很有必要。今天介绍常见的两种并发模型:共享内存&Actor

共享内存


面向对象编程中,万物都是对象,数据+行为=对象


多核时代,可并行多个线程,但是受限于资源对象,线程之间存在对共享内存的抢占/等待,实质是多线程调用对象的行为方法,这涉及#线程安全#线程同步#。

8b16f6d46e9f3af12be415a4e5252b80.png


假如现在有一个任务,找100000以内的素数的个数,如果用共享内存的方法,代码如下:


可以看到,这些线程共享了sum变量,对sumsum++操作时必须上锁。


using System;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
namespace Paralleler
{
    class Program
    {
        static object syncObj = new object();
        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            ShareMemory();
            sw.Stop();
            Console.WriteLine($"共享内存并发模型耗时:{sw.Elapsed}");
        }
        static void ShareMemory()
        {
            var sum = 0;
            Parallel.For(1, 100000 + 1,(x, state) =>
            {
                var f = true;
                if (x == 1)
                    f = false;
                for (int i = 2; i <= x / 2; i++)
                {
                    if (x % i == 0)  // 被[2,x/2]任一数字整除,就不是质数
                        f = false;
                }
                if(f== true)
                {
                    lock(syncObj)
                    {
                        sum++;   // 共享了sum对象,“++”就是调用sum对象的成员方法
                    }
                }
            });
            Console.WriteLine($"1-100000内质数的个数是{sum}");
        }
    }
}


共享内存更贴合"面向对象开发者的固定思维", 强调线程对于资源的掌控力。


Actor模型


Actor模型则认为一切皆是Actorshare nothing Actor模型内部的状态由自己的行为维护,外部线程不能直接调对象的行为,必须通过消息才能激发行为,也就是消息传递机制来代替共享内存模型对成员方法的调用, 这样保证Actor内部数据只能被自己修改


Actor模型= 数据+行为+消息。

6f533386212d3c89b6fe6dedd1fe8c10.png


还是找到100000内的素数,我们使用.NET TPL Dataflow来完成,代码如下:


每个Actor的产出物就是流转到下一个Actor的消息。


using System;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks.Dataflow;
using System.Diagnostics;
namespace Paralleler
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Actor();
            sw.Stop();
            Console.WriteLine($"Actor并发模型耗时:{sw.Elapsed}");  
        }
        static void Actor()
        {
            var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
            var transfromBlock = new TransformBlock<int,bool>(x=> 
            {
                var f = true;
                if (x == 1)
                    f = false;
                for (int i = 2; i <= x / 2; i++)
                {
                    if (x % i == 0)  // 被[2,x/2]任一数字整除,就不是质数
                        f = false;
                }
                return f;
            }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism =50 });
            var sum = 0;
            var actionBlock = new ActionBlock<bool>(x=>
            {
                if (x == true)
                    sum++;
            },new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1   });//这里必须使用默认并发度
            transfromBlock.LinkTo(actionBlock, linkOptions);
            // 准备从pipeline头部开始投递
            for (int i = 1; i <= 100000; i++)
            {
                transfromBlock.Post(i);
            }
            transfromBlock.Complete();  // 通知头部,不再投递了; 会将信息传递到下游。
            actionBlock.Completion.Wait();  // 等待尾部执行完成
            Console.WriteLine($"1-100000内质数的个数是{sum}");
        }
    }
}


Actor并发模型强调的是消息触发。


还不过瘾


共享内存模型:其实是并行线程调用对象的成员方法,这里不可避免存在加锁/解锁, 需要开发者自行关注线程同步、线程安全。


Actor模型以流水线管道的形式,各Actor独立挨个处理各自专属业务,等待消息流入。


我也很容易推断,每个Actor的伪代码实现:存在循环,不断处理新流入的消息。


var queue= new Queue(1000); 
 for{
    if(queue.Dequeue() != null) {
       // Done bussiness 
    }
    Thread.Sleep(50ms);
 }


所以Actor模型,开发者不用关注线程锁,同时,Actor模型解耦了调用关系,天然适合分布式场景。


ebba5ab963368d1e6c798f632f319c0a.png


总结陈词


1.何为“并发模型”,模型是达成某个方案的编程风格,共享内存/Actor并发模型说不上孰优孰劣,适用场景有偏向。


2.共享内存并发模型,更强调多线程对于资源的掌控力。


3.从概念上得知,Actor模型强调消息触发,更适合分布式场景,解耦了调用方和提供方(我这里演示的TPL Dataflow是进程内Actor模型)  


  TPL Dataflow组件应对高并发,低延迟要求


4.Golang使用的Channel类Actor模型,使用Channel进一步解耦了调用的参与方,你都不用关注下游提供者是谁。 Waiting For Next Article......


作为一名编程老兵,深知大家平时常用的是共享内存并发模型,开口闭口“多线程”,“锁”, 可能很多人并没有关注到Actor模型,微软进程内Actor TPL Dataflow香气侧漏,值得推荐。


多对比、多体验不同的编程风格,别有洞天。

相关文章
|
SQL 开发框架 .NET
共享内存 & Actor并发模型到底哪个快?
请大家仔细对比结论和下图,脱离场景和硬件环境谈性能就是耍流氓,理解不同并发模型的风格和能力是关键, 针对场景和未来的拓展性、可维护性、可操作性做技术选型 。
共享内存 & Actor并发模型到底哪个快?
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
291 1
|
22天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
1月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
22 3
|
1月前
|
存储 缓存 监控
Elasticsearch集群JVM调优堆外内存
Elasticsearch集群JVM调优堆外内存
50 1
|
1月前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
89 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
63 2

热门文章

最新文章

相关实验场景

更多