首页 文章

单例同步C.

提问于
浏览
2

如果我必须在C中编写单例类,我将使用静态变量,私有构造函数和返回类对象的公共静态函数 . 但是在多线程环境中,代码会出现问题 . 为了避免多个线程同时访问同一个变量,Boost线程是用于同步的最佳机制吗?我的意思是在资源上设置/取消设置锁/互斥锁 . 在C标准库中还有其他内置的东西,我不需要下载boost,构建东西等吗?我听说过C Ox,但不太了解 .

2 回答

  • 7

    C 98/03完全不支持线程 . 如果您正在使用C 98或03编译器,那么您几乎不会使用Boost,或某些(或多或少)特定于操作系统的东西,例如pthreads或Win32的线程原语 .

    C 11有一个相当完整的线程支持库,有互斥锁,锁,线程本地存储等 .

    然而,我觉得有必要指出,备份并做更多考虑是否需要/想要一个Singleton可能会更好 . 说实话,单身模式在很大程度上已经失宠了 .

    编辑:重读这个,我有点想跳过一件事:至少当我使用它们时,任何/所有单例都在任何辅助线程启动之前完全初始化 . 这引起了对初始化中线程安全性的关注,这完全没有实际意义 . 我想在你启动辅助线程之前你可能有一个你无法初始化的单例,所以你需要处理这个问题,但至少对它来说这是一个非常不寻常的例外,我只会在/如果绝对必要 .

  • 0

    对我来说,使用c 11实现单例的最佳方法是:

    class Singleton
    {
    public:
    static Singleton & Instance()
    {
        // Since it's a static variable, if the class has already been created,
        // It won't be created again.
        // And it **is** thread-safe in C++11.
    
        static Singleton myInstance;
    
        // Return a reference to our instance.
        return myInstance;
    }
    
    // delete copy and move constructors and assign operators
    Singleton(Singleton const&) = delete;             // Copy construct
    Singleton(Singleton&&) = delete;                  // Move construct
    Singleton& operator=(Singleton const&) = delete;  // Copy assign
    Singleton& operator=(Singleton &&) = delete;      // Move assign
    
    // Any other public methods
    
    protected:
    Singleton() 
    {
       // Constructor code goes here.
    }
    
    ~Singleton()
    {
        // Destructor code goes here.
    }
    
     // And any other protected methods.
    }
    

    这是一个c 11功能,但通过这种方式,您可以创建一个线程安全的Singleton . 根据新标准,不再需要关心这个问题 . 对象初始化只能由一个线程完成,其他线程将等待它完成 . 或者你可以使用std :: call_once .

    如果要对单例资源进行独占访问,则必须对这些函数使用锁定 .

    不同类型的锁:

    使用 atomic_flg_lck:

    class SLock
    {
    public:
      void lock()
      {
        while (lck.test_and_set(std::memory_order_acquire));
      }
    
      void unlock()
      {
        lck.clear(std::memory_order_release);
      }
    
      SLock(){
        //lck = ATOMIC_FLAG_INIT;
        lck.clear();
      }
    private:
      std::atomic_flag lck;// = ATOMIC_FLAG_INIT;
    };
    

    使用 atomic:

    class SLock
    {
    public:
      void lock()
      {
        while (lck.exchange(true));
      }
    
      void unlock()
      {
        lck = true;
      }
    
      SLock(){
        //lck = ATOMIC_FLAG_INIT;
        lck = false;
      }
    private:
      std::atomic<bool> lck;
    };
    

    使用 mutex:

    class SLock
    {
    public:
      void lock()
      {
        lck.lock();
      }
    
      void unlock()
      {
        lck.unlock();
      }
    
    private:
      std::mutex lck;
    };
    

    仅适用于 Windows

    class SLock
    {
    public:
      void lock()
      {
        EnterCriticalSection(&g_crit_sec);
      }
    
      void unlock()
      {
        LeaveCriticalSection(&g_crit_sec);
      }
    
      SLock(){
        InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
      }
    
    private:
      CRITICAL_SECTION g_crit_sec;
    };
    

    atomicand atomic_flg_lck 使线程保持旋转计数 . Mutex 只是睡觉了 . 如果等待时间太长也许最好睡眠线程 . 最后一个“ CRITICAL_SECTION ”使线程保持旋转计数直到消耗时间,然后线程进入休眠状态 .

    如何使用这些关键部分?

    unique_ptr<SLock> raiilock(new SLock());
    
    class Smartlock{
    public:
      Smartlock(){ raiilock->lock(); }
      ~Smartlock(){ raiilock->unlock(); }
    };
    

    使用raii成语 . 用于锁定关键部分的构造函数和用于解锁它的析构函数 .

    class Singleton {
    
       void syncronithedFunction(){
          Smartlock lock;
          //.....
       }
    
    }
    

    此实现是线程安全且异常安全的,因为变量锁保存在堆栈中,因此当函数作用域结束(函数结束或异常)时,将调用析构函数 .

    我希望你觉得这很有帮助 .

    谢谢!!

相关问题