热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#锁定基于类属性

如何解决《C#锁定基于类属性》经验,为你挑选了1个好方法。

我已经看过很多lock用法示例,它通常是这样的:

private static readonly object obj = new object();

lock (obj)
{
    // code here
}

是否可以根据类的属性进行锁定?我不想在全球锁与方法调用的任何lock声明,我想锁定功能只有在作为参数传递的对象具有相同的属性值,将其被在此之前,处理的另一对象.

那可能吗?这有道理吗?

这就是我的想法:

public class GmailController : Controller
{

    private static readonly ConcurrentQueue queue = new ConcurrentQueue();

    [HttpPost]
    public IActionResult ProcessPushNotification(PushRequest push)
    {
        var existingPush = queue.FirstOrDefault(q => q.Matches(push));
        if (existingPush == null)
        {
            queue.Enqueue(push);
            existingPush = push;
        }
        try
        {
            // lock if there is an existing push in the
            // queue that matches the requested one
            lock (existingPush)
            {
                // process the push notification
            }
        }
        finally
        {
            queue.TryDequeue(out existingPush);
        }
    }
}

背景:我有一个API,当我们的用户发送/接收电子邮件时,我会从Gmail的API接收推送通知.但是,如果有人同时向两个用户发送消息,我会收到两个推送通知.我的第一个想法是在插入之前查询数据库(基于主题,发件人等).在极少数情况下,第二次调用的查询是SaveChanges在前一次调用之前进行的,因此我最终会有重复项.

我知道,如果我想扩大规模,lock就会变得毫无用处.我也知道我可以创建一个工作来检查最近的条目并消除重复,但我尝试了不同的东西.欢迎任何建议.



1> Eric Lippert..:

我首先要确保我理解该提案.给出的问题是我们有一些资源共享给多个线程,调用它database,并且它允许两个操作:Read(Context)Write(Context).该提议是基于上下文的属性具有锁粒度.那是:

void MyRead(Context c) 
{
  lock(c.P) { database.Read(c); }
}
void MyWrite(Context c)
{
  lock(c.P) { database.Write(c); }
}

所以现在如果我们调用MyRead,其中context属性具有值X,并且调用MyWrite,其中context属性具有值Y,并且两个调用在两个不同的线程上竞争,则它们不是序列化的.但是,如果我们有两次调用MyWrite和调用MyRead,并且在所有这些调用,context属性的值为Z,则这些调用将序列化.

可能吗?是.这不是一个好主意.如上所述,这是一个坏主意,你不应该这样做.

了解为什么这是一个坏主意是有益的.

首先,如果属性是值类型(如整数),则会失败.您可能会认为,我的上下文是一个ID号,它是一个整数,我想使用ID号123序列化对数据库的所有访问,并使用ID号345序列化所有访问,但不会对每个访问序列化这些访问其他.锁仅适用于引用类型,而装箱值类型总是会为您提供一个新分配的框,因此即使ID相同,也不会对锁进行争用.它将完全被打破.

其次,如果属性是字符串,则会严重失败.锁在逻辑上通过引用 "比较" ,而不是通过.使用盒装整数,您总能得到不同的引用.使用字符串,您有时会得到不同的引用!(因为实习不一致.)你可能处于锁定"ABC"的情况,有时 "ABC"上的另一个锁等待,有时它不会!

但是,这是破的基本规则是:你必须永远在对象上锁定,除非该对象已被专门设计为一个锁定对象,并控制访问锁定的资源控制访问锁定对象相同的代码.

这里的问题不是锁定的"本地",而是全局的.假设你的财产是一个Frob地方Frob是引用类型. 您不知道进程中的任何其他代码是否也锁定了相同的代码Frob,因此您不知道需要哪些锁定顺序约束来防止死锁.程序是否死锁是程序的全局属性.就像你可以用实心砖建造一个空心房子一样,你可以用一系列单独正确的锁来构建一个死锁程序.通过确保每个锁仅在您控制私有对象上取出,您可以确保没有其他人锁定您的某个对象,因此分析您的程序是否包含死锁变得更加简单.

请注意,我说"更简单"而不是"简单".它将它简化为几乎不可能正确,从字面上看不可能得到正确.

所以,如果你一直在努力做到这一点,那么这样做的正确方法是什么?

正确的方法是实现一个新服务:一个锁对象提供者. LockProvider需要能够哈希比较相等的两个Ts.它提供的服务是:你告诉它你想要一个特定值的锁对象T,它会为你提供规范的锁对象T.当你完成后,你说你已经完成了.提供程序保留一个引用计数,指出它发出锁定对象的次数以及它返回的次数,并在计数变为零时从字典中删除它,这样我们就不会有内存泄漏.

显然,锁提供程序需要是线程安全的,并且需要极低的争用,因为它是一种旨在防止争用的机制,所以最好不要引起争用!如果这是你打算走下去的道路,你需要有一个C#线程专家来设计和实现这个对象.这很容易弄错.正如我在您的帖子的评论中所指出的那样,您正在尝试将并发队列用作一种糟糕的锁定提供程序,并且它是大量的竞争条件错误.

这是在所有.NET编程中纠正的一些最难的代码.我已经成为一名.NET程序员已有近20年的时间并实现了部分编译器,我认为自己无法胜任这些东西.寻求实际专家的帮助.


@Alisson:这个注释太短,无法解释如何在.NET中实现锁对象,但短版本是:**.NET中每个引用类型的对象都有一个在其内存映像中保留的同步块**,同步块用于存储在该对象上取出锁定时所需的信息.所以底层机制不需要比较两个锁是否相等; 它只是在同步块中查看是否有任何东西.这就是为什么锁始终绑定到*特定对象实例*; 信息实际上在对象中.
推荐阅读
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • win10电脑蓝屏代码0x000000a5无法进入系统解决方法详解
    许多用户在使用电脑的时候遇到蓝屏问题,重启无法进入系统。本文提供了解决方法:调整BIOS设置、禁用安全启动、重装系统等。如果以上方法都无法解决问题,需要重新安装一个系统。详细步骤请参考正文内容。 ... [详细]
author-avatar
Laiio120669
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有