真实世界并发软件中的读写示例

Ric*_*ian 2 concurrency multithreading semaphore locking

我正在寻找在并发系统中需要对相同值进行读写访问的真实世界示例.

在我看来,存在许多信号量或锁定,因为没有已知的替代方法(对于实现者),但是你知道任何模式似乎需要互斥量吗?

在某种程度上,我要求考虑现实世界中并发软件的标准HARD问题集.

dth*_*rpe 5

使用什么类型的锁取决于多个线程如何访问数据.如果您可以对用例进行微调,有时可以完全消除对独占锁的需求.

仅当您的用例要求共享数据必须始终100%准确时,才需要排他锁.这是大多数开发人员开始使用的默认设置,因为这是我们正常思考数据的方式.

不过,如果你正在使用可以容忍一些"松动"的数据是什么,有几种技术,而不在每次访问使用排它锁的线程之间共享数据.

例如,如果您有一个链接的数据列表,并且如果在列表遍历中多次查看同一节点而不会扰乱您对该链接列表的使用,并且如果在插入后没有立即看到插入,则不会感到不安(或类似的器物),您可以执行列表插入和删除利用原子指针交换,而无需插入周围句号互斥锁或删除操作.

另一个例子:如果你有一个主要从线程读取的数组或列表对象,并且只是偶尔由主线程更新,你可以通过维护列表的两个副本来实现无锁更新:一个是"活"的,而另一个是线程可以读取和另一个"离线",您可以在自己的线程中写入.要执行更新,请将"实时"列表的内容复制到"脱机"列表中,执行对脱机列表的更新,然后使用原子指针交换将脱机列表指针交换到实时列表指针.然后,您将需要一些机制让读者从现在离线列表中"消耗".在垃圾收集系统中,您可以释放对脱机列表的引用 - 当最后一个使用者完成它时,它将是GC.在非GC系统中,您可以使用引用计数来跟踪仍在使用列表的读者数量.对于此示例,仅将一个线程指定为列表更新程序将是理想的.如果需要多个更新程序,则需要锁定更新操作,但仅限于序列化更新程序 - 没有锁定,也不会对列表的读者产生性能影响.

我所知道的所有无锁资源共享技术都需要使用原子交换(又名InterlockedExchange).这通常转换为CPU中的特定指令和/或硬件总线锁(x86汇编器中的读或写操作码上的锁定前缀),持续非常短的时间.在多进程系统上,原子交换可能会强制其他处理器上的高速缓存失效(双proc Pentium II就是这种情况),但我不认为这对当前的多核芯片来说是一个问题.即使有这些性能警告,无锁运行也比采用完整内核事件对象快得多.只需调用内核API函数就需要几百个时钟周期(切换到内核模式).

现实场景的示例:

  1. 生产者/消费者工作流程.Web服务接收数据的http请求,将请求放入内部队列,工作线程从队列中提取工作项并执行工作.队列是可读/写的,必须是线程安全的.
  2. 线程之间共享的数据与所有权的更改.线程1分配一个对象,将其抛给线程2进行处理,并且永远不想再看到它.线程2负责处理对象.内存管理系统(malloc/free)必须是线程安全的.
  3. 文件系统.这几乎总是一个OS服务,并且已经完全是线程安全的,但它值得包括在列表中.
  4. 参考计数.当引用数降至零时释放资源.增量/减量/测试操作必须是线程安全的.这些通常可以使用原子基元而不是全停式内核互斥锁来实现.