重写 C++ STL 分配器函数以将其包装为线程安全

Overriding C++ STL allocator function to wrap it as threadsafe

本文关键字:包装 线程 安全 C++ STL 分配器 函数 重写      更新时间:2023-10-16

我的旧版C++代码库在多线程执行场景中失败,当两个线程同时尝试为 STL 向量分配内存时。下面给出了两个线程的堆栈跟踪。

-----------------  lwp# 4 / thread# 4  --------------------
 00136e10 allocate__t24__default_alloc_template2b0i0Ui (4, 4, 0, 0, 0, 0) + a4
 0015ee80 allocate__t12simple_alloc2ZP12ReconInitRecZt24__default_alloc_template2b0i0Ui (1, 0, 0, 0
, 0, 0) + 20
 0015da24 _M_allocate__t18_Vector_alloc_base3ZP12ReconInitRecZt9allocator1ZP12ReconInitRecb1Ui (86f
204, 1, 0, 0, 0, 0) + 10
 0014f438 _M_insert_aux__t6vector2ZP12ReconInitRecZt9allocator1ZP12ReconInitRecPP12ReconInitRecRCP1
2ReconInitRec (86f204, 0, fbe7fd04, 0, 0, 0) + cc
 0015f0a8 push_back__t6vector2ZP12ReconInitRecZt9allocator1ZP12ReconInitRecRCP12ReconInitRec (86f20
4, fbe7fd04, 0, 0, 0, 0) + 5c
 0015dbc0 push_back__t26ReconListOfRecFromMMapFile1ZP12ReconInitRecRCP12ReconInitRec (86f1f0, fbe7f
d04, 0, 0, fc551200, ff120400) + 28
 0017ca48 preprocess__8ReconCmpRCt26ReconListOfRecFromMMapFile1ZP12ReconInitRecb (86f160, 975100, 0
, b4, b4, 1) + 268
<<other stacktrace details truncated>>     
. . . .
 ff1494f0 _lwp_start (0, 0, 0, 0, 0, 0)
-----------------  lwp# 5 / thread# 5  --------------------
 ff14d1e4 _lwp_kill (6, 0, ff1b5010, ff12c928, ffffffff, 6) + 8
 ff0c1bac abort    (1f2b24, 1, 264ae4, ee930, ff1b34d8, 0) + 110
 001f2ca8 corehandler__7CdrSigsi (a, 0, fbaff4a8, 1, fedb2c40, cb4) + 184
 ff14961c __sighndlr (a, 0, fbaff4a8, 1f2b24, 0, 1) + c
 ff13dce8 call_user_handler (a, 0, 4, 0, fc551a00, fbaff4a8) + 3b8
 ff13ded0 sigacthandler (a, 0, fbaff4a8, 0, 0, 0) + 60
 --- called from signal handler with signal 10 (SIGBUS) ---
 00136e10 allocate__t24__default_alloc_template2b0i0Ui (8, 8, fbaffa38, 2d, 0, 0) + a4
 00126300 allocate__t12simple_alloc2Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc
_template2b0i0Zt24__default_alloc_template2b0i0Ui (2, 4, ffffffff, fbaffa38, 2d, 80808080) + 20
 00126338 _M_allocate__t18_Vector_alloc_base3Zt12basic_string3ZcZt18string_char_traits1ZcZt24__defa
ult_alloc_template2b0i0Zt9allocator1Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc
_template2b0i0b1Ui (86f2a0, 2, 2d, fbaffa65, fbaffa65, 1) + 10
 00133768 _M_insert_aux__t6vector2Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_t
emplate2b0i0Zt9allocator1Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b
0i0Pt12basic_string3ZcZt18string_char_traits1ZcZt24__defa (86f2a0, 8730b4, fbaffa20, fcbf4062, fbaf
fa65, 7fffffd2) + fc
 00157808 push_back__t6vector2Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_templ
ate2b0i0Zt9allocator1Zt12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0R
Ct12basic_string3ZcZt18string_char_traits1ZcZt24__default (86f2a0, fbaffa20, 7ffffc00, fcbf4062, 1c
, 2d) + 5c
 002064b8 getFiles__7ListDirb (86f280, 0, 253800, 4, 1c00, 1d3c) + 12c
<<other stacktrace details truncated>>     
. . . .

从堆栈跟踪中,它显示了对allocate__t24__default_alloc_template2b0i0Ui的并发调用,即 __default_alloc_template<false, 0>::allocate(unsigned int)失败,因为这不是线程安全的。此分配函数由函数调用

simple_alloc<X, __default_alloc_template<false, 0> >::allocate(unsi
gned int)

反过来又被调用

_Vector_alloc_base<X, allocator<X>, true>::_M_allocate
(unsigned int)

我想覆盖这三个函数之一,fpr 覆盖C++ STL 分配器函数,以将其包装为在互斥保证下受线程安全保护。请指教。

您应该同步对容器的访问,而不是使容器线程安全!如果你试图重新发明轮子,它最终会陷入噩梦。不仅有插入和删除操作,还有元素复制和移动以及对存储元素的并发操作。

事实上,我相信你的设计

是破碎的,容器分配器只表明你的应用程序有设计缺陷。

顺便说一句:也许你可以使用已经实现的线程安全容器,而不是重新发明轮子:

http://libcds.sourceforge.net/

http://www.boost.org/doc/libs/1_55_0/doc/html/lockfree.html

备注:我从未使用过它们,也不能说任何关于鲁棒性或其他质量问题的事情。