首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >不变性是否完全消除了多处理器编程中对锁的需求?

不变性是否完全消除了多处理器编程中对锁的需求?
EN

Software Engineering用户
提问于 2012-10-24 10:29:00
回答 4查看 13.7K关注 0票数 40

第1部分

显然,在多处理器编程中,不变性最小化了对锁的需求,但它是否消除了这种需求,或者是否存在仅存在不可变性还不够的情况?在我看来,在大多数程序不得不实际做一些事情(更新数据存储、生成报告、抛出异常等等)之前,您只能推迟处理和封装状态。这样的行为是否总是没有锁的呢?仅仅是抛出每个对象并创建一个新对象,而不是改变原始对象(不变性的粗糙视图)的行为,是否提供了绝对的保护,以避免进程间的争用,还是仍然需要锁定的角落情况?

我知道许多函数式程序员和数学家喜欢谈论“没有副作用”,但在“现实世界”,一切都有副作用,即使执行机器指令所需的时间也是如此。我对理论/学术答案和实际/现实世界的答案都感兴趣。

如果不变性是安全的,在一定的界限或假设下,我想知道“安全区”的边界到底是什么。一些可能的界限的例子:

  • I/O
  • 例外/错误
  • 与用其他语言编写的程序的交互
  • 与其他机器(物理、虚拟或理论)的交互

特别感谢@JimmaHoffa的他的评论,它启动了这个问题!

第2部分

多处理器编程经常被用作一种优化技术--使一些代码运行得更快。何时才能更快地使用锁和不可变对象?

考虑到Amdahl定律中规定的限制,您什么时候可以通过不变的对象和锁定可变的对象来获得更好的总体性能(不管是否考虑了垃圾收集器)?

摘要

我将这两个问题结合在一起,试图找到边界框作为线程问题的解决方案的不可变性的位置。

EN

回答 4

Software Engineering用户

发布于 2012-10-24 20:25:46

一个函数接受某些值并返回其他值,并且不干扰函数之外的任何内容,没有副作用,因此线程是安全的。如果您想考虑函数执行的方式如何影响耗电量,那是另一个问题。

我假设您所指的是一台图灵完整的机器,它正在执行某种定义良好的编程语言,其中实现细节是不相关的。换句话说,如果我用我所选择的编程语言编写的函数能够保证在语言范围内的不可变性,那么堆栈所做的事情就不重要了。当我用高级语言编程时,我不考虑堆栈,也不应该这样做。

为了说明这是如何工作的,我将在C#中提供几个简单的例子。为了使这些例子成为真的,我们必须做几个假设。首先,编译器没有错误地遵循C#规范,其次,它生成正确的程序。

假设我想要一个简单的函数,它接受一个字符串集合,并返回一个字符串,该字符串是集合中所有字符串的连接,由逗号分隔。C#中的一个简单、天真的实现可能如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public string ConcatenateWithCommas(ImmutableList<string> list)
{
    string result = string.Empty;
    bool isFirst = false;

    foreach (string s in list)
    {
        if (isFirst)
            result += s;
        else
            result += ", " + s;
    }
    return result;
} 

这个例子是不可变的,表面上看。我怎么知道的?因为string对象是不可变的。然而,实施并不理想。因为result是不可变的,所以每次都必须通过循环创建一个新的string对象,替换result指向的原始对象。这可能会对速度产生负面影响,并给垃圾收集器带来压力,因为垃圾收集器必须清理所有这些额外的字符串。

现在,假设我这样做:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public string ConcatenateWithCommas(ImmutableList<string> list)
{
    var result = new StringBuilder();
    bool isFirst = false;

    foreach (string s in list)
    {
        if (isFirst)
            result.Append(s);
        else
            result.Append(", " + s);
    }
    return result.ToString();
} 

注意,我已经将string result替换为可变对象StringBuilder。这比第一个示例要快得多,因为不是每次都通过循环创建一个新字符串。相反,StringBuilder对象只是将每个字符串中的字符添加到一个字符集合中,并在末尾输出整个过程。

即使StringBuilder是可变的,这个函数是不可变的吗?

是的,是这样的。为什么?因为每次调用此函数时,都会创建一个新的StringBuilder,仅用于该调用。所以现在我们有了一个纯函数,它是线程安全的,但是包含可变的组件。

但如果是我干的呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Concatenate
{
    private StringBuilder result = new StringBuilder();
    bool isFirst = false;

    public string ConcatenateWithCommas(ImmutableList<string> list)
    {
        foreach (string s in list)
        {
            if (isFirst)
                result.Append(s);
            else
                result.Append(", " + s);
        }
        return result.ToString();
    } 
}

这种方法线程安全吗?不,不是。为什么?因为这个类现在保存的是我的方法所依赖的状态。在该方法中现在存在一个争用条件:一个线程可以修改IsFirst,但是另一个线程可以执行第一个Append(),在这种情况下,我现在在字符串的开头有一个逗号,这个逗号不应该在那里。

我为什么要这样做?好吧,我可能希望线程将字符串累积到我的result中,而不考虑顺序,也不考虑线程进入的顺序。也许是个伐木工人,谁知道呢?

无论如何,为了修复它,我在方法的内部放置了一个lock语句。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Concatenate
{
    private StringBuilder result = new StringBuilder();
    bool isFirst = false;
    private static object locker = new object();

    public string AppendWithCommas(ImmutableList<string> list)
    {
        lock (locker)
        {
            foreach (string s in list)
            {
                if (isFirst)
                    result.Append(s);
                else
                    result.Append(", " + s);
            }
            return result.ToString();
        }
    } 
}

现在又是线程安全了。

我的不可变方法可能无法实现线程安全的唯一方法是,如果该方法以某种方式泄漏了它的部分实现。会发生这种事吗?如果编译器是正确的,程序是正确的,则不会。我会需要这样的方法的锁吗?不是的。

关于如何在并发场景中泄漏实现的示例,请看这里

票数 15
EN

Software Engineering用户

发布于 2012-10-24 13:03:58

我不确定我是否理解你的问题。

答案是肯定的。如果您的所有对象都是不可变的,那么您不需要任何锁。但是,如果您需要保留一个状态(例如,您实现了一个数据库,或者您需要聚合来自多个线程的结果),那么您需要使用可变性,因此也需要锁定。不可变性消除了对锁的需求,但通常您无法负担完全不可变的应用程序。

对第2部分的回答-锁应该总是比没有锁慢。

票数 4
EN

Software Engineering用户

发布于 2013-12-28 10:53:50

将一组相关状态封装在一个不可变对象的单个可变引用中,可以使用该模式执行多种状态修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
do
{
   oldState = someObject.State;
   newState = oldState.WithSomeChanges();
} while (Interlocked.CompareExchange(ref someObject.State, newState, oldState) != oldState;

如果两个线程都试图同时更新someObject.state,那么两个对象都将读取旧状态,并确定新状态将是什么,而不会彼此更改。执行CompareExchange的第一个线程将存储它认为下一个状态应该是什么。第二个线程将发现状态不再与它先前读取的状态匹配,因此,随着第一个线程的更改生效,将重新计算系统的适当下一个状态。

这种模式的优点是,被拦截的线程不能阻止其他线程的进程。它的进一步优势是,即使有激烈的竞争,一些线程将始终取得进展。但是,它的缺点是,在存在争用的情况下,许多线程可能会花费大量的时间来做工作,而这些工作最终会被丢弃。例如,如果单独CPU上的30个线程都试图同时更改一个对象,那么一个线程将在第一次尝试中成功,一个线程将在第二次尝试中成功,另一个线程将在第三个线程上成功,因此每个线程平均每个线程将进行15次更新其数据的尝试。使用“咨询”锁可以显著改善情况:在线程尝试更新之前,应该检查是否设置了“争用”指示符。如果是这样的话,它应该在进行更新之前获得一个锁。如果一个线程在更新中做了几次失败的尝试,它应该设置争用标志。如果试图获取锁的线程发现没有其他人在等待,则应该清除争用标志。请注意,这里的锁不是“正确性”所必需的;即使没有它,代码也会正确工作。锁的目的是尽量减少代码在不太可能成功的操作上花费的时间。

票数 4
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/171253

复制
相关文章
iOS进阶之runtime作用
前言 Runtime基本是用C和汇编写的,可见苹果为了动态系统的高效而作出的努力。你可以在这里下到苹果维护的开源代码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。Objective-C 从三种不同的层级上与 Runtime 系统进行交互,分别是通过 Objective-C 源代码,通过 Foundation 框架的NSObject类定义的方法,通过对 runtime 函数的直接调用。大部分情况下你就只管写你的Objc代码就行,runtime 系统自动在幕后辛勤劳作着。
Dwyane
2018/05/22
6380
React Native 和iOS Simulator 那点事
React Native 和iOS Simulator 那点事 尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://www.devio.org) 本文出自《React Native学习笔记》@http://www.devio.org系列文章。 问题1:使用React Native时按cmd+r无法reload js,cmd+d无法唤起 React Native开发菜单? 不知大家是否有过这样的经历,用 React Native开发应用正不亦乐乎的时候,突然发现,cmd+r,cmd+
CrazyCodeBoy
2018/05/07
2.1K0
React Native 和iOS Simulator 那点事
指针在函数中的作用
指针传递地址时,指针变量产生了副本,但副本与原变量所指的内存区域是同一个。对指针副本指向的变量进行改变,就是改变原指针变量所指向的变量。
跋扈洋
2022/04/27
2.9K0
指针在函数中的作用
iOS减包实战:Compress PNG Files作用分析
减包这个词大家应该都不陌生,在减包过程中,图片资源的优化这项应该是必经之路了。
天天P图攻城狮
2018/12/04
4.3K0
iOS减包实战:Compress PNG Files作用分析
在iOS上推流
编译librtmp需要用到openssl,所以先编译openssl,下载脚本OpenSSL。
Helloted
2022/06/07
8720
在iOS上推流
Spring Security 在 Servlet 的作用区域
Spring Security 使用标准的 Servlet 过滤器(Filter) 并与 Servlet 容器集成。 这个意味着 Spring Security 可以在任何运行运行在 Servlet 容器(Servlet Container)中的应用上使用。 更具体地说,你可以不使用 Spring,而是基于 Servlet 的应用程序中使用 Spring Security。
HoneyMoose
2022/09/30
3150
Spring Security 在 Servlet 的作用区域
虚拟变量在模型中的作用
实际场景中,有很多现象不能单纯的进行定量描述,只能用例如“出现”“不出现”这样的形式进行描述,这种情况下就需要引入虚拟变量。例如即将到来的女生节,每年的这个时候毛绒玩具的销量都会上升,说明女生节对毛绒玩具的销量产生了一定影响,但是这个影响程度又很难界定,这时只能定义一个虚拟变量去描述事情“发生”与“不发生”了。
许卉
2019/07/15
4.3K0
ZooKeeper在HBase集群中的作用
ZooKeeper作为分布式协调组件,在大数据领域的其他分布式组件中往往扮演着重要的辅助角色,因此我们就算不单独去研究ZooKeeper,也短不了要接触它。本文就以最典型的HBase为例,简要介绍ZooKeeper为HBase提供了哪些功能。
Flink实战剖析
2022/04/20
1.4K0
ZooKeeper在HBase集群中的作用
WiFi在物联网中的作用
The-Role-of-Wi-Fi-in-the-IoT-Space-1536x944-1.jpg
用户4122690
2020/06/28
1.7K0
WiFi在物联网中的作用
Lua组件在Redis中的作用
这种功能允许用户在Redis服务器上执行原子性的操作,从而避免了多次网络往返的开销。
一凡sir
2023/10/03
2890
Lua组件在Redis中的作用
Etcd在kubernetes集群中的作用
Etcd是Kubernetes集群中的一个十分重要的组件,用于保存集群所有的网络配置和对象的状态信息。在后面具体的安装环境中,我们安装的etcd的版本是v3.1.5,整个kubernetes系统中一共有两个服务需要用到etcd用来协同和存储配置,分别是:
菲宇
2019/06/12
3.8K0
AI在抗击疫情中的作用
这几十年以来,肆虐全球的新冠病毒可以说是最具感染性了。正如我们目前所见,新冠病毒已经给全世界造成了极大的破坏,因此,借助科技的力量抵抗疫情也变得十分必要。AI就是我们要说的重要的科技手段。本文将讨论AI在抗击新冠疫情中的重要作用。
人工智能小咖
2020/04/16
1.3K0
AI在抗击疫情中的作用
Redis在秒杀场景的作用
秒杀可分成秒杀前、秒杀中和秒杀后三阶段,每个阶段的请求处理需求不同,Redis具体在秒杀场景的哪个环节起到作用呢?
JavaEdge
2023/02/13
7620
Redis在秒杀场景的作用
KubeEdge及其在MEC中的作用
KubeEdge是一个云原生计算基金会(CNCF)sandbox项目,旨在将Kubernetes从云扩展到边缘。它提供了基础架构支持,以支持在边缘节点上部署和编排云原生服务,以及边缘与云之间元数据的同步。
CNCF
2020/07/16
1.6K0
iOS的开发中相关证书的理解及作用
我们都知道开发iOS应用是少不了苹果证书的,对于一个新手来说,这个是比较头疼的是,毕竟真机测试,发布蒲公英测试,苹果提供的内测testflight,上传到app-store都要跟苹果证书打交道,上面这些步骤最好就是自己走一遍,不然你对苹果的开发证书的制作流程还是会一头雾水,其实说到底就是证书对应测试环境和发布环境,配置证书也是对应测试环境和发布环境,然后你需要创建一个app ID去把证书和配置文件联系起来。
青年码农
2020/10/13
2.1K0
iOS的开发中相关证书的理解及作用
在 MvvmCross 下使用 iOS Storyboard
可能是因为上面的缺点吧, Mvx 没有提供内置的 Storyboard 支持。 不过 Mvx 的扩展性是很强大的, 通过自定义 MvxTouchViewsContainer 就可以实现对 Storyboard 的支持。
beginor
2020/08/10
7160
在 MvvmCross 下使用 iOS Storyboard
spring在ssh框架中的作用学习
spring在ssh框架中的作用学习 在SSH框假中spring充当了管理容器的角色。我们都知道Hibernate用来做持久层,因为它将JDBC做了一个良好的封装,程序员在与数据库进行交互时可以不用书写大量的SQL语 句。Struts是用来做应用层的,他它负责调用业务逻辑serivce层。所以SSH框架的流程大致是:Jsp页面—-Struts——Service(业务逻辑处理类)—Hibernate(左到右) struts负责控制Service(业务逻辑处理类),从而控制了Service的生命
用户1289394
2018/02/26
1.2K0
【iOS开发】使用 protocol 与 extension 来限制函数作用域
今天碰到这样一个场景,我需要一个仅仅用来展示网页的页面,那么在 iOS 9 中,我可以使用 SFSafariViewController,而在 iOS 8 及之前版本中,我会使用一个 WebView 来占据整个视图空间,来做这件事。
KyXu
2019/04/11
5560
【夯实基础】Spring在ssh中的作用
尊重版权:http://blog.csdn.net/qjlsharp/archive/2009/03/21/4013255.aspx
全栈程序员站长
2022/07/07
6940
点击加载更多

相似问题

MaterializeCSS select()方法在IOS上不起作用

144

MaterializeCSS图标在Angular 7的导航栏上不起作用

19

TouchableOpacity在ios上不起作用

31

.on("click")在iOS上不起作用

22

VideoJS:在IOS上不起作用

40
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文