【问题标题】:Custom allocators vs. promises and packaged tasks自定义分配器与承诺和打包任务
【发布时间】:2015-03-20 02:29:27
【问题描述】:

标准promise/packaged_task 的分配器采用构造函数是否应该仅将分配器用于状态对象本身,或者应该为所有(内部)相关对象保证这一点?

[futures.promise]: "...为共享状态分配内存"
[futures.task.members]: "...分配存储内部数据结构所需的内存"

特别是以下错误或功能吗?

*MSVC 2013.4,Boost 1.57,short_alloc.h by Howard Hinnant

示例 1

#define BOOST_THREAD_VERSION 4
#include <boost/thread/future.hpp>
#include "short_alloc.h"
#include <cstdio>

void *operator new( std::size_t s ) {
    printf( "alloc %Iu\n", s );
    return malloc( s );
}

void operator delete( void *p ) {
    free( p );
}

int main() {

    const int N = 1024;
    arena< N > a;
    short_alloc< int, N > al( a );

    printf( "[promise]\n" );
    auto p = boost::promise< int >( std::allocator_arg, al );
    p.set_value( 123 );

    printf( "[packaged_task]\n" );
    auto q = boost::packaged_task< int() >( std::allocator_arg, al, [] { return 123; } );
    q();

    return 0;

}

输出:

...
[promise]
alloc 8
alloc 12
alloc 8
alloc 24
[packaged_task]
alloc 8
alloc 12
alloc 8
alloc 24

FWIW,默认分配器的输出是

...
[promise]
alloc 144
alloc 8
alloc 12
alloc 8
alloc 16
[packaged_task]
alloc 160
alloc 8
alloc 12
alloc 8
alloc 16

示例 2

AFAICT,MSVC 的std::mutex 进行了不可避免的堆分配,因此使用它的std::promise 也是如此。这是符合标准的行为吗?

【问题讨论】:

  • 您为什么在示例代码中使用boost,并谈论std 指定的行为?为什么不使用std
  • @Yakk:另外,我确实尝试过标准;参见示例 2。实际上,我先尝试了 std,然后转而使用 boost,希望能更多地使用自定义分配器 --- 但唉。

标签: visual-c++ c++11 boost language-lawyer c++14


【解决方案1】:

注意您的代码有几个问题。在 C++14 中,如果您替换 operator delete(void*),那么您还必须替换 operator delete(void*, std::size)t)。您可以使用功能测试宏来查看编译器是否需要:

void operator delete( void *p ) {
    free( p );
}
#if __cpp_sized_deallocation
// Also define sized-deallocation function:
void operator delete( void *p, std::size_t ) {
    free( p );
}
#endif

其次,size_t 的正确 printf 格式说明符是 zu 而不是 u,因此您应该使用 %Izu

AFAICT,MSVC 的std::mutex 进行了不可避免的堆分配,因此使用它的std::promise 也是如此。这是符合标准的行为吗?

std::mutex 是否应该使用动态分配当然值得怀疑。它的构造函数不能,因为它必须是constexpr。它可能会延迟分配直到第一次调用lock()try_lock()lock() 没有将获取资源失败列为有效错误条件,这意味着try_lock() 可能无法锁定非竞争互斥锁,如果它无法分配所需的资源。如果您眯着眼睛看,这是允许的,但并不理想。

但是关于你的主要问题,正如你所引用的,标准只对promise 这么说:

第二个构造函数使用分配器a为共享状态分配内存。

这并没有说明承诺所需的其他资源。可以合理地假设任何像互斥锁这样的同步对象都是共享状态的一部分,而不是承诺,但是这种措辞并不要求分配器用于共享状态的成员所需的内存,仅用于共享状态所需的内存自己。

对于packaged_task,措辞更宽泛,暗示所有内部状态都应该使用分配器,尽管可以争辩说这意味着分配器用于为存储的任务和共享状态获取内存,但同样是成员的共享状态不必使用分配器。

总之,我不认为标准是 100% 明确是否允许 MSVC 实现,但恕我直言,不需要来自 mallocnew 的额外内存的实现更好(这就是 libstdc++ &lt;future&gt; 实现工作)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-03
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-02
    相关资源
    最近更新 更多