经过测试:
Windows x86_64(MSVS 2013和2015)
Linux x86_64(g ++ 6.3.0和clang 3.8.0)
此代码在在线编译器中:http : //coliru.stacked-crooked.com/a/a97a84ff26c6e9ee
将safe_ptr <>与rw-lock一起使用。
要将安全指针与rw-lock而不是unique-lock一起使用,只需 #include <safe_ptr.h> 编写so shared_mutex_safe_ptr<std::map<int,int>>而不是即可 safe_ptr<std::map<int,int>>。或者更好地编写contfree_safe_ptr<std::map<int,int>> ptr;使用更快的共享互斥量的方法,我们将在第二篇文章中对其进行描述。然后,使用 slock_safe_ptr(ptr)->find(5);或auto const& ptr_read = ptr; ptr_read->find(5); 称为只读共享锁-我们将在第三篇文章中介绍这些方式。
其他背景
可组合性和僵局。
由于我们在上文中将锁用于线程安全,因此我们的算法称为基于锁。
在无锁容器中没有死锁,基于事务性内存的算法方面是否真的还不错,并且现代RDBMS中是否存在死锁:MSSQL(基于锁的IL)和Oracle(多版本并发控制)?
无锁算法不允许一次原子地更改多个容器。RDBMS的死锁问题与基于锁的算法相同,它们通常通过锁超时或锁图来解决。并且C ++标准中新的事务安全部分不允许您安全使用复杂算法,例如std :: map <>。
无锁算法不具有可组合操作的属性-几种无锁算法的联合原子使用。也就是说,无法一次更改或读取多个无锁数据结构。例如,您可以使用来自libCDS的关联数组的无锁容器,它们将分别是线程安全的。但是,如果您想一次用几个无锁容器自动执行操作并保持一致性,则不能这样做,因为它们的API不能同时在多个容器上提供无锁操作的功能。当您更改或读取一个容器时,那时将已经更改另一个容器。为避免这种情况,您将需要使用锁,在这种情况下,它将是基于锁的容器,这意味着它们将具有基于锁的算法的所有问题,即可能出现死锁的问题。另外,有时仅在使用一个容器的情况下使用锁:
http://libcds.sourceforge.net/doc/cds-api/namespacecds_1_1sync.html
https://github.com/khizmax/libcds/tree/master/cds/lock
https://github.com/khizmax/libcds/tree/master/cds/sync
在事务性RDBMS中,例如MSSQL(基于锁)和Oracle(多版本并发控制),也使用锁,这就是死锁存在问题的原因,例如,可以通过构建锁来自动解决死锁问题。图形并找到循环等待,或通过设置锁定等待时间,从tbl中选择col,其中(…)中的id用于更新等待30;如果经过了潜在时间或在锁定图中发现了死锁,则发生事务之一的回滚-也就是说,取消该事务已经进行的所有更改,解锁所有已锁定的事务; 然后您可以尝试从一开始就执行事务(如此多次):
Oracle数据库在线文档11g第1版(11.1)-死锁:https : //docs.oracle.com/cd/B28359_01/server.111/b28318/consist.htm#i5337
Oracle锁:https : //docs.oracle.com/cd/B28359_01/server.111/b28318/consist.htm#i5249
如您所见,任何表达式都使用排他的不兼容锁:Insert / Update / Delete / Select-For-Update
MSSQL检测和结束死锁:https 😕/technet.microsoft.com/zh-cn/library/ms178104( v= sql.105).aspx
MS SQL锁:https://technet.microsoft.com/zh-cn/library/ms186396(v = sql.105).aspx
反过来,与无锁容器不同,事务性内存可以自动处理许多容器/数据。也就是说,事务性内存具有可组合操作功能。在内部,使用了悲观锁(具有死锁冲突可能性)或乐观锁(更可能是具有竞争性修改的冲突修改)。并且在发生任何冲突的情况下,交易从一开始就自动取消并重复,这需要多次重复执行所有操作-这会导致高昂的间接费用。他们正在尝试通过在CPU级别上创建硬件事务性存储器来降低开销成本,但是到目前为止,尽管英特尔已经在Haswell CPU中添加了硬件事务性存储器,但仍没有实现令人满意的性能的实现。他们还承诺将事务性内存包含在C ++标准中,但仅在将来,到目前为止,它仅用作实验性,不支持使用std :: map。也就是说,到目前为止,一切仅在理论上是好的。但是在将来,这很可能会取代通常的同步方法。
最终:
如果在实现时未提供这样的选项,则不构成基于锁的算法。但是可以实现此选项,我们在前面的部分中已成功实现。
无锁算法不是组合的,没有锁的组合是非常复杂的任务。但是使用锁时,这种算法将不再是无锁的,并且存在永久死锁的风险。减肥食谱:www.sheonline.cn
RDBMS:MSSQL(基于锁的IL)和Oracle(MVCC)-可能存在死锁,可以通过锁图或超时来消除死锁。
到目前为止,来自C ++标准实验部分的事务性内存仅限于仅在最简单的算法中使用,并且不允许在std :: map <>方法或更复杂的算法中使用算法。
结论:死锁问题存在于表现出高性能的所有类型的算法和系统中,其中一次调用多个数据结构。因此,我们提供了2种解决方案来解决safe_ptr <>
静态link_safe_ptrs tmp_link(safe_user_accounts,safe_cash_flows_src_id,safe_cash_flows_dst_id); -将一个互斥锁用于多个容器
lock_timed_any_infinity lock_all(safe_cash_flows_src_id,safe_cash_flows_dst_id,safe_user_accounts); -使用锁超时;时间到期后,解锁所有内容并尝试再次锁定它
如果只有一个容器和一个递归互斥锁用于safe_ptr <>,则在safe_ptr <>中不会发生死锁,因为我们至少需要2个递归互斥锁来进行死锁(或1个非递归锁)。