C++工具:STL容器多线程接口封装--多线程接口安全

该文介绍了一种针对C++STL容器的多线程接口封装方法,通过在接口上加锁确保在多线程环境下的安全性,包括queue_th、map_th和vector_th。虽然不保证迭代器的安全,但提供了如push、pop、insert等关键操作的线程安全版本。文章通过示例展示了如何使用这些封装后的容器,并警告在大规模多线程场景下可能需要选择真正的线程安全容器。
摘要由CSDN通过智能技术生成
//!
//! C++工具:STL容器多线程接口封装--多线程接口安全
//!
//! ===== 多线程容器简介 =====
//! 多线程安全容器指的是在多个线程同时操作同一个容器时,总是保证数据同步的,
//!     但C++STL标准容器中不提供线程安全的容器,保证线程安全需要消耗更多的时间等待操作,
//!     本章封装了STL容器中的部分容器,采用保护继承的方式尽最大可能保留原始STL容器接口
//! 主要封装方式为对所有接口加锁,保证容器接口线程安全,但不保证迭代器的线程安全,
//!     多线程操作迭代器存在迭代器易位情况, 如果需要操作迭代器需要自行加锁
//! 因为STL容器存在可保存的迭代器直接指向内存地址,导致很难保证线程安全,
//!     如果需要实现线程安全的容器,迭代器不应该保留,如此以来也失去了操作数据的灵活性
//! ===== 多线程容器简介 =====
//!
//!
//! ===== 提供的封装容器 =====
//!     queue_th    : queue  管道容器代表
//!     map_th      : map    索引容器代表
//!     vector_th   : vector 线性容器代表
//! ===== 提供的封装容器 =====
//!
//!
//! ===== 迭代器易位 =====
//! 迭代器易位的场景,如vector容器在多线程下扩容,导致其他线程的迭代器全部失效,
//!     或者容器数据改变,导致迭代器的数据顺序改变,如map的insert函数被连续调用,
//!     导致其他线程提前保存的begin迭代器可能改变位置,迭代器失效
//! ===== 迭代器易位 =====
//!
//!
//! ===== 内容展示顺序 =====
//! 1.queue_th.h
//! 2.map_th.h
//! 3.vector_th.h
//! 4.main.h (包含容器测试函数)
//! ===== 内容展示顺序 =====
//!
//! 结束语:
//!     可以提供简易的多线程容器应用场景,但是不保证所有操作的安全性,
//!         如果需要大量多线程的场景请选择真正线程安全的容器
//!
#ifndef QUEUE_TH_H
#define QUEUE_TH_H

#include <queue>
#include <deque>
#include <mutex>
#include <memory>

//!
//! 功能: 对queue容器加锁,保证多线程下调用接口安全,不保证迭代器安全
//! 原理:保护继承queue容器,实现大部分函数接口, 使用继承构造获取大部分父类构造函数
//!
template<typename _Tp, typename _Sequence = std::deque<_Tp> >
class queue_th : protected std::queue<_Tp,_Sequence>
{
public:
    //C++STD
    typedef typename	_Sequence::value_type		value_type;
    typedef typename	_Sequence::reference		reference;
    typedef typename	_Sequence::size_type		size_type;
    //this class
    typedef std::queue<_Tp,_Sequence>       parent;
    typedef std::lock_guard<std::mutex>     lock_t;

public:
    using std::queue<_Tp,_Sequence>::queue;

    //拷贝底层deque容器的数据到queue_th,需要覆盖父类的拷贝构造和赋值重载
    explicit queue_th(const queue_th& __q) : parent(__q) {}
    explicit queue_th(queue_th&& __q) : parent(std::move(__q)) {}
    void operator=(const queue_th& __q) { this->c = __q.c; }
    void operator=(queue_th&& __q) { this->c = std::move(__q.c); }

    void pop() { lock_t lock(_mutex); this->parent::pop(); }
    reference back() { lock_t lock(_mutex); return this->parent::back(); }
    reference front() { lock_t lock(_mutex); return this->parent::front(); }
    size_type size() { lock_t lock(_mutex); return this->parent::size(); }
    bool empty() { lock_t lock(_mutex); return this->parent::empty(); }

    void swap(queue_th& __q)
    { lock_t lock(_mutex); std::swap(this->c, __q.c); }

    void push(const value_type& __x)
    { lock_t lock(_mutex);  this->parent::push(__x); }

    void push(value_type&& __x)
    { lock_t lock(_mutex);  this->parent::push(std::move(__x)); }

    template<typename... _Args>
    void emplace(_Args&&... __args)
    { lock_t lock(_mutex); this->parent::emplace(std::forward<_Args>(__args)...); }

protected:
    std::mutex _mutex;
};
#endif // QUEUE_TH_H
#ifndef MAP_TH_H
#define MAP_TH_H

#include <map>
#include <mutex>
#include <memory>

//!
//! 功能: 对map容器加锁,保证多线程下调用接口安全,不保证迭代器安全
//! 原理:保护继承map容器,实现大部分函数接口, 使用继承构造获取大部分父类构造函数
//!
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
      typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map_th : protected std::map<_Key,_Tp,_Compare,_Alloc>
{
public:
    typedef _Key                            key_type;
    typedef _Tp                             mapped_type;
    typedef std::pair<const _Key, _Tp>		value_type;
    typedef _Compare                        key_compare;
    typedef _Alloc                          allocator_type;

    typedef typename __gnu_cxx::__alloc_traits<_Alloc>::
            template rebind<value_type>::other                          _Pair_alloc_type;
    typedef std::_Rb_tree<key_type, value_type, std::_Select1st<value_type>,
             key_compare, _Pair_alloc_type>                             _Rep_type;

    typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type>         _Alloc_traits;
    typedef typename _Alloc_traits::pointer                     pointer;
    typedef typename _Alloc_traits::const_pointer               const_pointer;
    typedef typename _Alloc_traits::reference                   reference;
    typedef typename _Alloc_traits::const_reference             const_reference;
    typedef typename _Rep_type::iterator                        iterator;
    typedef typename _Rep_type::const_iterator                  const_iterator;
    typedef typename _Rep_type::size_type                       size_type;
    typedef typename _Rep_type::difference_type                 difference_type;
    typedef typename _Rep_type::reverse_iterator                reverse_iterator;
    typedef typename _Rep_type::const_reverse_iterator          const_reverse_iterator;

    //this class
    typedef std::map<_Key,_Tp,_Compare,_Alloc>      parent;
    typedef std::lock_guard<std::mutex>             lock_t;

public:
    //继承构造函数,并覆盖父类的拷贝构造
    using std::map<_Key,_Tp,_Compare,_Alloc>::map;
    map_th(const map_th& __m) : parent(__m) {}
    map_th(map_th&& __m) : parent(std::move(__m)) {}

    mapped_type& at(const key_type& __k)
    { lock_t lock(_mutex); return parent::at(__k); }

    mapped_type& operator[](const key_type& __k)
    { lock_t lock(_mutex); return parent::operator[](__k); }

    mapped_type& operator[](key_type&& __k)
    { lock_t lock(_mutex); return parent::operator[](std::forward<key_type>(__k)); }

    iterator begin() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::begin(); }

    iterator end() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::end(); }

    reverse_iterator rbegin() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::rbegin(); }

    reverse_iterator rend() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::rend(); }

    iterator find(const key_type& __x)
    { lock_t lock(_mutex); return parent::find(__x); }

    void clear() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::clear(); }

    iterator erase(const_iterator __position)
    { lock_t lock(_mutex); return parent::erase(__position); }

    iterator erase(iterator __position)
    { lock_t lock(_mutex); return parent::erase(__position); }

    size_type erase(const key_type& __x)
    { lock_t lock(_mutex); return parent::erase(__x); }

    iterator erase(const_iterator __first, const_iterator __last)
    { lock_t lock(_mutex); return parent::erase(__first,__last); }

    std::pair<iterator, bool> insert(const value_type& __x)
    { lock_t lock(_mutex); return parent::insert(__x); }

    std::pair<iterator, bool> insert(value_type&& __x)
    { lock_t lock(_mutex); return parent::insert(move(__x)); }

    template<typename _Pair>
    std::__enable_if_t<std::is_constructible<value_type, _Pair>::value,std::pair<iterator, bool>>
    insert(_Pair&& __x)
    { lock_t lock(_mutex); return parent::insert(std::forward<_Pair>(__x)); }

    void insert(std::initializer_list<value_type> __list)
    { lock_t lock(_mutex); return parent::insert(__list); }

    iterator insert(const_iterator __position, const value_type& __x)
    { lock_t lock(_mutex); return parent::insert(__position,__x); }

    iterator insert(const_iterator __position, value_type&& __x)
    { lock_t lock(_mutex); return parent::insert(__position,move(__x)); }

    template<typename _Pair>
    std::__enable_if_t<std::is_constructible<value_type, _Pair>::value, iterator>
    insert(const_iterator __position, _Pair&& __x)
    { lock_t lock(_mutex); return parent::insert(__position,std::forward<_Pair>(__x)); }

    template<typename _InputIterator>
    void insert(_InputIterator __first, _InputIterator __last)
    { lock_t lock(_mutex); return parent::insert(__first,__last); }

    template<typename... _Args>
    std::pair<iterator, bool> emplace(_Args&&... __args)
    { lock_t lock(_mutex); return parent::emplace(std::forward<_Args>(__args)...); }

    template<typename... _Args>
    iterator emplace_hint(const_iterator __pos, _Args&&... __args)
    { lock_t lock(_mutex); return parent::emplace_hint(__pos,std::forward<_Args>(__args)...); }

    _GLIBCXX_NODISCARD bool empty() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::empty(); }

    size_type size() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::size(); }

    size_type max_size() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::max_size(); }

protected:
    std::mutex _mutex;
};
#endif // MAP_TH_H
#ifndef VECTOR_TH_H
#define VECTOR_TH_H

#include <vector>
#include <mutex>
#include <memory>

//!
//! 功能: 对vector容器加锁,保证多线程下调用接口安全,不保证迭代器安全
//! 原理:保护继承vector容器,实现大部分函数接口, 使用继承构造获取大部分父类构造函数
//!
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector_th : protected std::vector<_Tp, _Alloc>
{
public:
    //this class
    typedef std::lock_guard<std::mutex>                             lock_t;
    typedef std::vector<_Tp, _Alloc>                                vector;
    typedef std::vector<_Tp, _Alloc>                                parent;
    //C++STD
    typedef std::_Vector_base<_Tp, _Alloc>                          _Base;
    typedef typename _Base::_Tp_alloc_type                          _Tp_alloc_type;
    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type>               _Alloc_traits;
    typedef _Tp                                                     value_type;
    typedef typename _Base::pointer                                 pointer;
    typedef typename _Alloc_traits::const_pointer                   const_pointer;
    typedef typename _Alloc_traits::reference                       reference;
    typedef typename _Alloc_traits::const_reference                 const_reference;
    typedef __gnu_cxx::__normal_iterator<pointer, vector>           iterator;
    typedef __gnu_cxx::__normal_iterator<const_pointer, vector>     const_iterator;
    typedef std::reverse_iterator<const_iterator>                   const_reverse_iterator;
    typedef std::reverse_iterator<iterator>                         reverse_iterator;
    typedef size_t                                                  size_type;
    typedef ptrdiff_t                                               difference_type;
    typedef _Alloc                                                  allocator_type;

    //this class
    using std::vector<_Tp, _Alloc>::vector;
    //C++STD
    using _Base::_M_allocate;
    using _Base::_M_deallocate;
    using _Base::_M_impl;
    using _Base::_M_get_Tp_allocator;

public:
    explicit vector_th(const vector_th& __x) : parent(__x) {}
    explicit vector_th(vector_th&& __x) : parent(std::move(__x)) {}

    reference operator[](size_type __n) _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return this->parent::operator[](__n); }

    reference at(size_type __n)
    { lock_t lock(_mutex); return parent::at(__n); }

    iterator begin() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::begin(); }

    iterator end() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::end(); }

    reverse_iterator rbegin() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::rbegin(); }

    reverse_iterator rend() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::rend(); }

    reference back() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::back(); }

    _Tp* data() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::data(); }

    void clear() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); parent::clear(); }

    reference front() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::front(); }

    void swap(vector& __x) _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); parent::swap(__x); }

    iterator erase(const_iterator __first, const_iterator __last)
    { lock_t lock(_mutex); return parent::erase(__first,__last); }

    iterator erase(const_iterator __position)
    { lock_t lock(_mutex); return parent::erase(__position); }

    void assign(size_type __n, const value_type& __val)
    { lock_t lock(_mutex); parent::assign(__n,__val); }

    template<typename _InputIterator, typename = std::_RequireInputIter<_InputIterator>>
    void assign(_InputIterator __first, _InputIterator __last)
    { lock_t lock(_mutex); parent::assign(__first,__last); }

    void assign(std::initializer_list<value_type> __l)
    { lock_t lock(_mutex); parent::assign(__l); }

    iterator insert(const_iterator __position, const value_type& __x)
    { lock_t lock(_mutex); return parent::insert(__position,__x); }

    iterator insert(const_iterator __position, value_type&& __x)
    { lock_t lock(_mutex); return parent::insert(__position,std::move(__x)); }

    iterator insert(const_iterator __position, std::initializer_list<value_type> __l)
    { lock_t lock(_mutex); return parent::insert(__position,__l); }

    iterator insert(const_iterator __position, size_type __n, const value_type& __x)
    { lock_t lock(_mutex); return parent::insert(__position,__n,__x); }

    template<typename _InputIterator, typename = std::_RequireInputIter<_InputIterator>>
    iterator insert(const_iterator __position, _InputIterator __first, _InputIterator __last)
    { lock_t lock(_mutex); return parent::insert(__position,__first,__last); }

    void resize(size_type __new_size)
    { lock_t lock(_mutex); parent::resize(__new_size); }

    void resize(size_type __new_size, const value_type& __x)
    { lock_t lock(_mutex); parent::resize(__new_size,__x); }

    void reserve(size_type __n)
    { lock_t lock(_mutex); parent::reserve(__n); }

    void pop_back() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); parent::pop_back(); }

    void push_back(const value_type& __x)
    { lock_t lock(_mutex); parent::push_back(__x); }

    void push_back(value_type&& __x)
    { lock_t lock(_mutex); parent::push_back(std::move(__x)); }

    size_type size() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::size(); }

    size_type max_size() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::max_size(); }

    _GLIBCXX_NODISCARD
    bool empty() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::empty(); }

    template<typename... _Args>
    iterator emplace(const_iterator __position, _Args&&... __args)
    { lock_t lock(_mutex); return parent::emplace(__position,std::forward<_Args>(__args)...); }

    size_type capacity() _GLIBCXX_NOEXCEPT
    { lock_t lock(_mutex); return parent::capacity(); }

    template<typename... _Args>
    void emplace_back(_Args&&... __args)
    { lock_t lock(_mutex); parent::emplace_back(std::forward<_Args>(__args)...); }

protected:
    std::mutex _mutex;
};
#endif // VECTOR_TH_H
void test_3()
{
    //基本类型操作测试
    cout<<endl<<"===== queue_th ====="<<endl;
    queue_th<int> thq1;
    thq1.push(11);
    thq1.push(12);
    thq1.push(13);
    thq1.push(14);
    thq1.push(15);

    queue_th<int> thq2(thq1);
    queue_th<int> thq3;
    thq3 = std::move(thq2);

    cout<<"====="<<endl;
    while(thq3.empty() == false)
    {
        cout<<thq3.front()<<endl;
        thq3.pop();
    }

    //类操作测试
    queue_th<string> th1;
    th1.push("s11");
    th1.push("s22");
    th1.push("s33");
    th1.push("s44");
    string s55 = "s55";
    th1.push(s55);
    th1.emplace("s66");
    string s77 = "s77";
    th1.emplace(s77);

    queue_th<string> th2(std::move(th1));
    queue_th<string> th3;
    th3 = th2;

    cout<<"====="<<endl;
    while(th2.size() > 0)
    {
        cout<<th2.front()<<endl;
        th2.pop();
    }

    //多线程测试
    cout<<"====="<<endl;
    queue_th<string> queue_th_1;
    auto func_th = [&](){
        for(int i=0;i<100;i++)
        {
            string str = "pp: " + to_string(i);
            queue_th_1.push(str);
            queue_th_1.push(std::move(str));
        }
    };

    thread thread_1(func_th);
    thread thread_2(func_th);
    thread thread_3(func_th);
    thread_1.join();
    thread_2.join();
    thread_3.join();
    cout<<queue_th_1.size()<<endl;

    //容器赋值与交换测试
    queue_th<string> queue_th_2(queue_th_1);
    queue_th<string> queue_th_3;
    queue_th_3 = queue_th_1;

    queue_th<string> queue_th_tm(queue_th_1);
    queue_th<string> queue_th_4(std::move(queue_th_tm));

    cout<<"====="<<endl;
    cout<<queue_th_2.size()<<endl;
    cout<<queue_th_3.size()<<endl;
    cout<<queue_th_4.size()<<endl;

    queue_th<string> queue_th_null;
    cout<<"swap front: "<<queue_th_null.size()<<endl;

    queue_th_null.swap(queue_th_4);
    cout<<"swap after: "<<queue_th_null.size()<<endl;
}

void test_4()
{
    cout<<endl<<"===== vector_th ====="<<endl;
    vector_th<string> v1;
    v1.push_back("v1");
    v1.push_back("v2");
    string vv3 = "v3";
    v1.push_back(vv3);
    string vv4 = "v4";
    v1.push_back(std::move(vv4));
    v1.emplace_back("v5");

    vector_th<string> v2(v1);
    vector_th<string> vtm(v1);
    vector_th<string> v3(std::move(vtm));

    cout<<v1.size()<<endl;
    cout<<v2.size()<<endl;
    cout<<v3.size()<<endl;

    cout<<"====="<<endl;
    for(auto a:v2)
    {
        cout<<a<<endl;
    }

    cout<<"====="<<endl;
    for(auto it = v3.begin();it!=v3.end();it++)
    {
        cout<<*it<<endl;
    }

    cout<<"====="<<endl;
    cout<<v3.at(2)<<endl;
    cout<<v3[3]<<endl;

    cout<<"====="<<endl;
    v1.emplace(v1.begin(),"v0");
    cout<<"capacity: "<<v1.capacity()<<endl;
    cout<<"size: "<<v1.size()<<endl;

    cout<<"====="<<endl;
    for(auto it = v1.begin();it!=v1.end();it++)
    {
        cout<<*it<<endl;
    }

    cout<<"====="<<endl;
    vector_th<int> veci1;
    auto func_th = [&](){
        for(int i=0;i<10000;i++)
        {
            veci1.push_back(i);
        }
    };

    thread thread_1(func_th);
    thread thread_2(func_th);
    thread thread_3(func_th);
    thread_1.join();
    thread_2.join();
    thread_3.join();
    cout<<veci1.size()<<endl;
}

void test_5()
{
    cout<<endl<<"===== map_th ====="<<endl;
    int v2=20;
    int v3=30;
    int v5=50;
    int v6=60;

    //插入测试
    map_th<int,int> mm1;
    mm1.insert(pair<int,int>(1,10));
    mm1.insert(pair<int,int>(2,v2));
    mm1.insert(pair<int,int>(3,std::move(v3)));
    mm1.emplace(4,40);
    mm1.emplace(5,v5);
    mm1.emplace(6,std::move(v6));
    mm1.emplace_hint(mm1.begin(),pair<int,int>(7,70));

    for(const auto &a : mm1)
    {
        cout<<a.first<<"|"<<a.second<<endl;
    }
    cout<<mm1.size()<<endl;

    //赋值测试
    map_th<int,int> mm2(mm1);
    map_th<int,int> mm3(std::move(mm1));

    cout<<mm2.size()<<endl;
    cout<<mm3.size()<<endl;
    cout<<mm1.size()<<endl;

    auto it = mm2.begin();
    for(int i=0;i<3;i++) it++;
    mm2.erase(mm2.begin(),it);
    cout<<mm2.size()<<endl;

    //多线程测试
    map_th<int,string> ms1;
    ms1.emplace(11,"ss1");
    ms1.emplace(12,"ss2");
    ms1.emplace(13,"ss3");
    ms1.emplace(14,"ss4");

    atomic<int> count {0};
    auto func = [&](){
        for(int i=100;i<1000;i++)
        {
            count++;
            ms1.emplace(i,"push");
        }
    };

    thread t1(func);
    thread t2(func);
    thread t3(func);
    t1.join();
    t2.join();
    t3.join();

    cout<<ms1.size()<<endl;
    cout<<count<<endl;
}

#include <unistd.h>
void test_6()
{
    //迭代器易位测试
    cout<<endl<<"== 迭代器易位测试 =="<<endl;
    vector_th<string> v1;
    v1.reserve(10);
    v1.push_back("word_1");
    v1.push_back("word_2");
    v1.push_back("word_3");

    //初始化的数据量
    cout<<v1.capacity()<<"|"<<v1.size()<<endl;

    //拿到迭代器,延时时候使用(模拟多线程下被时间切片打断操作)
    auto func1 = [&](){
        auto it1 = v1.begin();
        cout<<"对比点: "<<*it1<<endl;
        sleep(1);   //延时使得多线程下一定被其他线程插入操作
        it1++;
        cout<<"对比点: "<<*it1<<endl; //多线程操作迭代器之后,迭代器易位,打印非法数据
    };

    //往容器添加数据(模拟多线程下迭代器失效)
    auto func2 = [&](){
        v1.insert(v1.begin()+1,"add word"); //其他线程插入操作
    };

    cout<<"单线程操作迭代器"<<endl;
    func1();
    func2();

    cout<<"===== show ====="<<endl;
    for(auto a:v1)
    {
        cout<<a<<endl;
    }

    v1.clear();
    v1.reserve(10);
    v1.push_back("word_1");
    v1.push_back("word_2");
    v1.push_back("word_3");

    cout<<endl<<"多线程操作迭代器"<<endl;
    thread th1(func1);
    thread th2(func2);
    th1.join();
    th2.join();

    cout<<"===== show ====="<<endl;
    for(auto a:v1)
    {
        cout<<a<<endl;
    }

    //多线程添加数据之后的数据量
    cout<<v1.capacity()<<"|"<<v1.size()<<endl;
}

int main()
{
    cout<<"===== begin ====="<<endl;
//    test_1();
//    test_2();
    test_3();
    test_4();
    test_5();
    test_6();
    cout<<"===== end ====="<<endl;
    return 0;
}


/*
 * 多线程容器接口测试
 *

===== begin =====

===== queue_th =====
=====
11
12
13
14
15
=====
s11
s22
s33
s44
s55
s66
s77
=====
600
=====
600
600
600
swap front: 0
swap after: 600

===== vector_th =====
5
5
5
=====
v1
v2
v3
v4
v5
=====
v1
v2
v3
v4
v5
=====
v3
v4
=====
capacity: 8
size: 6
=====
v0
v1
v2
v3
v4
v5
=====
30000

===== map_th =====
1|10
2|20
3|30
4|40
5|50
6|60
7|70
7
7
7
0
4
904
2700

== 迭代器易位测试 ==
10|3
单线程操作迭代器
对比点: word_1
对比点: word_2
===== show =====
word_1
add word
word_2
word_3

多线程操作迭代器
对比点: word_1
对比点: add word
===== show =====
word_1
add word
word_2
word_3
10|4
===== end =====

*/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值