并发事件: ReaderWriterGate锁

有一天,我正在忙于一个咨询项目,忽然碰到了一个以前从未碰到过的线程同步问题。该公司正在创 建一个 Web 服务,并且服务器收到的客户端请求几乎均需要以只读方式存取某些共享数据。 有时,还会 收到需要修改共享数据的请求,同时我们需要使这些数据保持同步。这种情况听起来非常适合采用读写锁

然而,经过对项目的更深入研究,我了解到该公司的要求有一个不寻常的地方:当服务器接收到 Web 服务请求并且锁的状态为写时,该锁持续极长的一段时间。而当写线程修改共享数据时,要读这些数据的 其他 Web 服务请求需要进入服务器。这里的问题是池中的线程正在被唤醒以处理读请求,但是由于要等 待写线程完成其任务,因而所有读线程会很快转入休眠状态。

创建成百上千的池线程没有花费很 长的时间,并且所有这些线程在操作系统内核内部都处于等待状态。此时许多线程都无法运行,而只是白 白浪费资源(如每个线程的堆栈以及关联的线程内核对象)。这很糟糕。而且越来越糟!当写线程完成共 享数据的写操作时,它会解锁。然后锁看到有成百上千的读线程在等待着它,它便释放所有处于等待状态 的读线程。服务器计算机有四个 CPU,并且这四个小 CPU 必须处理所有这些可运行的线程的工作。 Windows 会使用其正常的轮询调度技术来调度所有这些线程,但将 CPU 不断地从一个线程切换到另 一个线程的开销会大大降低性能、可伸缩性和吞吐量。

这一问题困扰了我好几个月,直到最后我 设计了我自己的线程同步锁 ReaderWriterGate,这一问题才得到解决。该锁非常高效,并且可以非常好 地用于多种应用程序中。ReaderWriterGate 的有趣之处在于调用线程几乎从不中断,即使中断了,也能 保证它们只中断极短的一段时间。而且在内部,由于 ReaderWriterGate 使用的是我的 ResourceLock 派 生类之一(前面提到的 6 月的那个专栏中对此进行了讨论),我只更改一行代码即可让 ReaderWriterGate 在内部使用旋转锁,从而在 Windows 内核中,线程从不中断(我在 2005 年 10 月的 “并发事件”专栏中对此进行了介绍)。这样可以提高性能,不过为了公平起见,它会要求使 用 ReaderWriterGate 的所有线程都具有相同的线程优先级并禁用优先级提升功能。这将进一步提高性能 。此外,ReaderWriterGate 只使用少数几个线程便能够完成非常多的工作。因此,不管向该 Gate 发 出多少请求,所需的资源都非常少。

我用我自己的方法实现了这一功能,并将其作为 Power Threading 库的一部分免费提供,读者可从 www.wintellect.com 下载。如果您要利用我的实现方法,请 遵守该库附带的《最终用户许可协议》(EULA)。更多详细信息请参阅 EULA。

接下来我将对 ReaderWriterGate 背后的原理进行介绍,探讨一种可能的方法,并提供一种研究线程处理和线程同步的 新方式。我希望您能够看到在您的现有代码中可以应用此类思路的地方,从而尽可能少地重新设计架构, 您可以结合其中的某些理念,提高应用程序的性能和可伸缩性。

ReaderWriterGate 对象模型

要使用 ReaderWriterGate 类,只需了解以下几个方法:

public sealed class 

ReaderWriterGate
{
  public ReaderWriterGate();
  public void QueueWrite(
   ReaderWriterGateCallback callback, Object state);
  public void QueueRead(
   ReaderWriterGateCallback callback, Object state);
  ... // Some methods not shown to simplify the discussion
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索数据
, 线程
, 性能
, 同步
, 状态
一个
sreader writer uoml、reader writer uoml、readerwriteruoml.rar、java reader writer、reader writer,以便于您获取更多的相关知识。

时间: 2024-10-03 04:03:42

并发事件: ReaderWriterGate锁的相关文章

PHP并发 加悲观锁(1)

php如何解决多线程读写同一文件 大家都知道,PHP是没有多线程概念的,尽管如此我们仍然可以用"不完美"的方法来模拟多线程.简单的说,就是队列处理.通过对文件进行加锁和解锁,来实现.当一个文件被一个用户操作时,该文件是被锁定的,其他用户只能等待,确实不够完美,但是也可以满足一些要求不高的应用. 上限判断,关键数据的写入扣钱之类 用到了Eaccelerator的内存锁和文件锁,原理:判断系统中是否安了EAccelerator如果有则使用内存锁,如果不存在,则进行文件锁.根据带入的key的

并发事件:使用AsyncEnumerator简化APM

目录 使用 AsyncEnumerator 类 AsyncEnumerator 的体系结构 使用 Wait 和 Inbox 计数器 线程安全变形 了解更多信息 在上期专栏 (并发事件: 通过 C# 简化 APM) 中,,我讲述了一些有关如何使用新的 C# 语言功能( 匿名方法.lambda 表达式和迭代器)简化异步编程的理念.在专栏最后,我说明了如何借助 C# 迭代器 使用同步编程模型完成异步编程.图 1 显示了示例迭代器.不过,仅使用常规的 C# foreach 语句是无 法执行迭代器的代码的

线程同步工具(三)等待多个并发事件完成

声明:本文是< Java 7 Concurrency Cookbook>的第三章, 作者: Javier Fernández González 译者:郑玉婷 等待多个并发事件完成 Java并发API提供这样的类,它允许1个或者多个线程一直等待,直到一组操作执行完成. 这个类就是CountDownLatch类.它初始一个整数值,此值是线程将要等待的操作数.当某个线程为了想要执行这些操作而等待时, 它要使用 await()方法.此方法让线程进入休眠直到操作完成. 当某个操作结束,它使用countD

如何实现超高并发的无锁缓存?

一.需求缘起 [业务场景] 有一类写多读少的业务场景:大部分请求是对数据进行修改,少部分请求对数据进行读取. 例子1:滴滴打车,某个司机地理位置信息的变化(可能每几秒钟有一个修改),以及司机地理位置的读取(用户打车的时候查看某个司机的地理位置). void SetDriverInfo(long driver_id, DriverInfoi); // 大量请求调用修改司机信息,可能主要是GPS位置的修改 DriverInfo GetDriverInfo(long driver_id);  // 少

Java 高并发九:锁的优化和注意事项详解_java

摘要 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 锁优化的思路和方法 2. 虚拟机内的锁优化 3. 一个错误使用锁的案例 4. ThreadLocal及其源码分析 1. 锁优化的思路和方法 在[高并发Java 一] 前言中有提到并发的级别. 一旦用到锁,就说明这是阻塞式的,所以在并发度上一般来说都会比无锁的情况低一点. 这里提到的锁优化,是指在阻塞式的情况下,如何让性能不要变得太差.但是再怎么优化,一般来说性能都会比无锁的情况差一点. 这里要注意的是,在[高并

Mysql事务,并发问题,锁机制-- 幻读、不可重复读(转)

1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约束没有被破坏 持久性:事务的提交结果,将持久保存在数据库中   2.事务并发会产生什么问题 1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了. 例如: 张三的工资为5000,事务A中获取工资为5000,事务B获取工资

Oracle官方并发教程之锁对象

原文地址,译文地址,译者:李任,校对:郑旭东 同步代码依赖于一种简单的可重入锁.这种锁使用简单,但也有诸多限制.java.util.concurrent.locks包提供了更复杂的锁.我们不会详细考察这个包,但会重点关注其最基本的接口,锁. 锁对象作用非常类似同步代码使用的隐式锁.如同隐式锁,每次只有一个线程可以获得锁对象.通过关联Condition对象,锁对象也支持wait/notify机制. 锁对象之于隐式锁最大的优势在于,它们有能力收回获得锁的尝试.如果当前锁对象不可用,或者锁请求超时(如

并发事件: 通过C#简化APM

目录 匿名方法和 lambda 表达式 foreach 语句 迭代器 异步编程 我曾一直赞扬异步编程模型 (APM) 的优点,强调异步执行 I/O 密集型操作是生产高响应和可伸 缩应用程序及组件的关键.这些目标是可以达成的,因为 APM 可让您使用极少量的线程来执行大量的工 作,而无需阻止任何线程.遗憾的是,使用 APM 构建典型的应用程序或组件有些难度,因此许多程序员 不愿意去做. 有几个因素使得 APM 难以实现.首先,您需要避免状态数据驻留于线程的堆栈上, 因为线程堆栈上的数据不能迁移到其

并发事件: 实现CLR异步编程模型

通常 I/O 操作的特点是速度慢.不可预见.当应用程序执行同步 I/O 操作时,基本上会放弃对正在 完成实际工作的设备的控制.例如,如果应用程序调用 StreamRead 方法从 FileStream 或 NetworkStream 读取某些字节,我们无法预计该方法需要多长时间才能返回.如果正在被读取的文件位于 本地硬盘上,那么 Read 操作可能会立即返回.如果存储文件的远程服务器脱机,则 Read 方法可能会等 待几分钟,然后超时并引发异常.在此期间,发出同步请求的线程会被占用.如果该线程是