当前位置: 首页 > news >正文

坪山网站建设信息广州网站优化排名

坪山网站建设信息,广州网站优化排名,wap网站 web app,郑州做网站那使用C无锁编程实现多线程下的单例模式 贺志国 2023.8.1 在多线程环境下创建一个类的单例对象,要比单线程环境下要复杂很多。下面介绍在多线程环境下实现单例模式的几种方法。 一、尺寸较小的类单例对象创建 如果待创建的单例类SingletonForMultithread内包含的成…

使用C++无锁编程实现多线程下的单例模式

贺志国
2023.8.1

在多线程环境下创建一个类的单例对象,要比单线程环境下要复杂很多。下面介绍在多线程环境下实现单例模式的几种方法。

一、尺寸较小的类单例对象创建

如果待创建的单例类SingletonForMultithread内包含的成员变量较少,整个类占用的内存空间较小,则可使用局部静态变量来创建单例对象。C++ 11标准保证在进入多线程前,已完成静态类对象的构建。如果类的尺寸较大,静态变量存储栈区无法容纳该类的单例对象,则禁止使用该方法。例如:64位Linux系统默认栈的最大空间为8 MB,64位Windows系统默认栈的最大空间为1 MB,当待创建的单例对象尺寸接近或超过上述栈的默认存储空间时,如使用该方法创建则会导致程序崩溃。示例代码如下所示:

class SmallSingletonForMultithread {public:static SmallSingletonForMultithread& GetInstance() {static SmallSingletonForMultithread instance;return instance;}private:SmallSingletonForMultithread() = default;~SmallSingletonForMultithread() = default;SmallSingletonForMultithread(const SmallSingletonForMultithread&) = delete;SmallSingletonForMultithread& operator=(const SmallSingletonForMultithread&) = delete;SmallSingletonForMultithread(SmallSingletonForMultithread&&) = delete;SmallSingletonForMultithread& operator=(SmallSingletonForMultithread&&) = delete;
};

二、尺寸较大的类单例对象创建(要求显式调用销毁函数来避免内存泄漏)

在实际工作中,由于某些单例类的尺寸较大,静态变量存储栈区无法容纳该单例对象,因此无法使用上述方法来创建单例对象,这时需要使用new在堆区动态创建单例对象。为了避免多线程环境下对于单例对象的抢夺,可使用C++无锁编程来实现。需要付出的代价就是,最后一个调用者需要显式地调用销毁函数DestoryInstance来避免内存泄漏,示例代码如下所示:

#include <atomic>
#include <cassert>
#include <mutex>class SingletonForMultithread {public:static SingletonForMultithread* GetInstance() {if (!instance_.load(std::memory_order_acquire)) {auto* new_ptr = new SingletonForMultithread;SingletonForMultithread* old_ptr = nullptr;if (!instance_.compare_exchange_strong(old_ptr, new_ptr,std::memory_order_release,std::memory_order_relaxed)) {// If the CAS operation fails, another thread has created a singleton// object, and it's necessary to delete the temporary object created by// the current thread.delete new_ptr;new_ptr = nullptr;}}return instance_.load(std::memory_order_relaxed);}static void DestoryInstance() {if (instance_.load(std::memory_order_acquire)) {auto* old_ptr = instance_.load(std::memory_order_relaxed);SingletonForMultithread* new_ptr = nullptr;if (instance_.compare_exchange_strong(old_ptr, new_ptr,std::memory_order_release,std::memory_order_relaxed)) {// If the CAS operation succeeds, the current thread obtains the// original object and can safely delete it.delete old_ptr;old_ptr = nullptr;}}}private:SingletonForMultithread() = default;~SingletonForMultithread() = default;SingletonForMultithread(const SingletonForMultithread&) = delete;SingletonForMultithread& operator=(const SingletonForMultithread&) = delete;SingletonForMultithread(SingletonForMultithread&&) = delete;SingletonForMultithread& operator=(SingletonForMultithread&&) = delete;private:static std::atomic<SingletonForMultithread*> instance_;
};// Static member variable initialization
std::atomic<SingletonForMultithread*> SingletonForMultithread::instance_;int main() {auto* singleton = SingletonForMultithread::GetInstance();assert(singleton != nullptr);singleton->DestoryInstance();return 0;
}

三、尺寸较大的类单例对象创建(使用std::unique_ptr<T>std::call_once实现)

很多时候,我们无法显式地调用销毁函数来避免内存泄漏,这时就可借助std::unique_ptr<T>std::call_once来实现,示例代码如下:

#include <cassert>
#include <memory>
#include <mutex>class SingletonForMultithread {public:~SingletonForMultithread() = default;static SingletonForMultithread* GetInstance() {static std::unique_ptr<SingletonForMultithread> instance;static std::once_flag only_once;std::call_once(only_once,[]() { instance.reset(new (std::nothrow) SingletonForMultithread); });return instance.get();}private:SingletonForMultithread() = default;SingletonForMultithread(const SingletonForMultithread&) = delete;SingletonForMultithread& operator=(const SingletonForMultithread&) = delete;SingletonForMultithread(SingletonForMultithread&&) = delete;SingletonForMultithread& operator=(SingletonForMultithread&&) = delete;
};int main() {auto* singleton = SingletonForMultithread::GetInstance();assert(singleton != nullptr);return 0;
}

但我在Ubuntu 20.04系统上使用GCC 9.4.0似乎无法正常完成任务,会抛出异常,产生core dump,原因暂不详。
gcc
core dump

四、尺寸较大的类单例对象创建(使用std::unique_ptr<T>std::atomic_flag实现)

第三节借助std::unique_ptr<T>std::call_once来实现单例对象的创建,同时避免显式地调用销毁函数来避免内存泄漏。这种方法在Ubuntu 20.04系统上使用GCC 9.4.0实现时似乎会导致程序core dump。于是我们使用std::atomic_flag替换std::call_once来完成任务。基本思想如下:首先定义一个静态的无锁标志变量std::atomic_flag start_flag,并将其初始值设置为ATOMIC_FLAG_INIT。第一次调用start_flag.test_and_set(std::memory_order_relaxed)函数时,由于start_flag的状态是ATOMIC_FLAG_INIT,该函数返回false,于是可调用instance.reset(new SingletonForMultithread)创建单例对象。第二次直至第N次调用start_flag.test_and_set(std::memory_order_relaxed)函数时,因为start_flag的状态已被设置,该函数返回true,创建单例对象的语句instance.reset(new SingletonForMultithread)永远不会被再次执行,这就达到了只创建一次的目的。同时,因为使用静态的智能指针变量std::unique_ptr<SingletonForMultithread> instance来管理单例对象,于是不再需要显式地回收内存,只要程序结束,静态变量自动清除,智能指针对象instance会在其析构函数中释放内存。

由于new运算符创建单例对象可能耗时较长,为了避免其他线程在单例对象创建到一半的过程中读取到不完整的对象,导致未定义的行为,我们使用另一个原子变量std::atomic<bool> finished来确保创建动作已正确完成,不选用另一个无锁标志变量std::atomic_flag的原因是,该类在C++ 20标准前未提供单独的测试函数testfinished.store(true, std::memory_order_release);while (!finished.load(std::memory_order_acquire))的内存顺序,实现了synchronizes-withhappens-before关系,保证在while (!finished.load(std::memory_order_acquire))成功时,instance.reset(new SingletonForMultithread);必定执行完毕,单例对象的创建是完整的。

完整的示例代码如下:

#include <atomic>
#include <cassert>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>using namespace std::chrono_literals;namespace {
constexpr size_t kThreadNum = 2000;
}class SingletonForMultithread {public:~SingletonForMultithread() = default;static SingletonForMultithread* GetInstance() {static std::unique_ptr<SingletonForMultithread> instance;static std::atomic_flag start_flag = ATOMIC_FLAG_INIT;static std::atomic<bool> finished(false);if (!start_flag.test_and_set(std::memory_order_relaxed)) {// The object created by the `new` operator may be relatively large and// time-consuming, therefore another atomic variable 'finished' is used to// ensure that other threads read a fully constructed singleton object. Do// not consider using another `std::atomic_flag`. Because it doesn't// provide a separate `test` function before the C++ 20 standard.instance.reset(new (std::nothrow) SingletonForMultithread);finished.store(true, std::memory_order_release);}// Wait in a loop until the singleton object is fully created, using// `std::this_thread::yield()` to save CPU resources.while (!finished.load(std::memory_order_acquire)) {std::this_thread::yield();}return instance.get();}private:SingletonForMultithread() {// Simulate a constructor that takes a relative long time.std::this_thread::sleep_for(10ms);}SingletonForMultithread(const SingletonForMultithread&) = delete;SingletonForMultithread& operator=(const SingletonForMultithread&) = delete;SingletonForMultithread(SingletonForMultithread&&) = delete;SingletonForMultithread& operator=(SingletonForMultithread&&) = delete;
};int main() {std::vector<std::thread> customers;for (size_t i = 0; i < kThreadNum; ++i) {customers.emplace_back(&SingletonForMultithread::GetInstance);}for (size_t i = 0; i < kThreadNum; ++i) {customers[i].join();}auto* singleton = SingletonForMultithread::GetInstance();assert(singleton != nullptr);return 0;
}
http://www.yidumall.com/news/50657.html

相关文章:

  • 做导购网站赚钱吗数字营销是干啥的
  • 做网站付费流程精准营销平台
  • 个人空间网站免费培训机构专业
  • 精品应用下载安装seo运营招聘
  • 做垃圾词影响网站排名吗搜索引擎营销的主要方式有
  • 东莞做网站公司广告推广投放平台
  • 免费咨询离婚律师在线福州seo快速排名软件
  • 网站怎样做自适应分辨率大小成功营销案例100例
  • 泉州市住房和城乡建设局网站网页搜索关键字
  • 湖北seo优化的含义
  • 网页设计与网站架设海口网站排名提升
  • 免费凡科建站官网河南网站网络营销推广
  • 光遇网页制作素材杭州seo网站优化公司
  • 品牌网站开发广州seo推广公司
  • 做图像网站2020年度关键词有哪些
  • 怎么样子做网站百家号排名
  • 长春网站建设yunbeiw手机百度问一问
  • 网站开发 运维 招投标网址外链平台
  • 动易网站后台修改栏目的字哪里可以免费推广广告
  • 自己在家开网店怎么开广州优化seo
  • wordpress斜杠新区seo整站优化公司
  • 金华企业网站建设公司关于网络营销的方法
  • 莱芜二中网站怎样在网上做宣传
  • 做网站还有流量么北京网站优化推广方案
  • 企业网站备案时间哈尔滨seo公司
  • 学生网页网站制作软件大全栾城seo整站排名
  • 什么是网站开发技术站长之家0
  • 网站制作和维护费用网站优化 seo和sem
  • 汉中微信网站建设公司网络营销型网站
  • 网站改版设计要多久网络策划与营销