【问题标题】:Implementing a spinlock in Boost. Example Needed在 Boost 中实现自旋锁。需要的例子
【发布时间】:2013-11-02 14:35:28
【问题描述】:

我想知道 boost 是否有任何库可以帮助实现自旋锁。我知道 boost 支持互斥锁,但我找不到任何显示或描述 boost 中的自旋锁的示例。任何显示如何使用 boost(最好)实现自旋锁的示例将不胜感激。(C++98)

【问题讨论】:

    标签: c++ boost spinlock c++98


    【解决方案1】:

    使用Boost.Atomic的示例:

    #include <boost/atomic.hpp>
    
    class SpinLock
    {
        boost::atomic_flag flag; // it differs from std::atomic_flag a bit -
                                 // does not require ATOMIC_FLAG_INIT
    public:
        void lock()
        {
            while( flag.test_and_set(boost::memory_order_acquire) )
                ;
        }
        bool try_lock()
        {
            return !flag.test_and_set(boost::memory_order_acquire);
        }
        void unlock()
        {
            flag.clear(boost::memory_order_release);
        }
    };
    

    LIVE Demo on Coliru

    #include <boost/range/algorithm.hpp>
    #include <boost/atomic.hpp>
    #include <boost/thread.hpp>
    #include <iostream>
    #include <vector>
    
    class SpinLock
    {
        boost::atomic_flag flag;
    public:
        void lock()
        {
            while( flag.test_and_set(boost::memory_order_acquire) )
                ;
        }
        bool try_lock()
        {
            return !flag.test_and_set(boost::memory_order_acquire);
        }
        void unlock()
        {
            flag.clear(boost::memory_order_release);
        }
    };
    
    int main()
    {
        using namespace std; using namespace boost;
    
        SpinLock lock;
        vector<thread> v;
        for(auto i = 0; i!=4; ++i)
            v.emplace_back([&lock, i]
            {
                for(auto j = 0; j!=16; ++j)
                {
                    this_thread::yield();
                    lock_guard<SpinLock> x(lock);
                    cout << "Hello from " << i << flush << "\tj = " << j << endl;
                }
            });
        for(auto &t: v)
            t.join();
    }
    

    输出是:

    Hello from 0    j = 0
    Hello from 1    j = 0
    Hello from 3    j = 0
    Hello from 2    j = 0
    Hello from 3    j = 1
    Hello from 1    j = 1
    Hello from 3    j = 2
    Hello from 2    j = 1
    Hello from 1    j = 2
    Hello from 2    j = 2
    Hello from 1    j = 3
    Hello from 2    j = 3
    Hello from 1    j = 4
    Hello from 3    j = 3
    Hello from 2    j = 4
    Hello from 1    j = 5
    Hello from 2    j = 5
    Hello from 1    j = 6
    Hello from 2    j = 6
    Hello from 1    j = 7
    Hello from 2    j = 7
    Hello from 1    j = 8
    Hello from 2    j = 8
    Hello from 3    j = 4
    Hello from 2    j = 9
    Hello from 3    j = 5
    Hello from 1    j = 9
    Hello from 2    j = 10
    Hello from 1    j = 10
    Hello from 2    j = 11
    Hello from 3    j = 6
    Hello from 1    j = 11
    Hello from 2    j = 12
    Hello from 3    j = 7
    Hello from 1    j = 12
    Hello from 2    j = 13
    Hello from 3    j = 8
    Hello from 2    j = 14
    Hello from 3    j = 9
    Hello from 1    j = 13
    Hello from 2    j = 15
    Hello from 1    j = 14
    Hello from 3    j = 10
    Hello from 1    j = 15
    Hello from 3    j = 11
    Hello from 3    j = 12
    Hello from 3    j = 13
    Hello from 3    j = 14
    Hello from 3    j = 15
    Hello from 0    j = 1
    Hello from 0    j = 2
    Hello from 0    j = 3
    Hello from 0    j = 4
    Hello from 0    j = 5
    Hello from 0    j = 6
    Hello from 0    j = 7
    Hello from 0    j = 8
    Hello from 0    j = 9
    Hello from 0    j = 10
    Hello from 0    j = 11
    Hello from 0    j = 12
    Hello from 0    j = 13
    Hello from 0    j = 14
    Hello from 0    j = 15
    

    【讨论】:

    • 从C++11开始有一个std::atomic_flag,可以用来代替这个实现。
    • @joaomlneto 是的,但问题是关于 Boost。
    【解决方案2】:

    这是一个使用 C++11 atomic 的示例:

    #include <atomic>
    
    typedef std::atomic<bool> Lock;
    
    void enterCritical(Lock& lock) {
        bool unlocked = false;
        while (!lock.compare_exchange_weak(unlocked, true));
    }
    
    void exitCritical(Lock& lock) {
        lock.store(false);
    }
    

    【讨论】:

    • 1.为此目的最好使用std::atomic_flag,因为它保证是无锁的。 2.有Boost.Atomic库,也有boost::atomic_flag
    • 但是 atomic_flag 接口不同:en.cppreference.com/w/cpp/atomic/atomic_flag -> 不能使用比较和交换,只能测试和设置。
    • re2:如果有 STL 等价物,为什么还要使用 Boost? (假设 C++11)
    • 我正在寻找一个 c++98 实现
    • @erenon 是的,atomic_flag 有不同的接口,但足以实现自旋锁。关于standard library(不是STL!) - 是的,当然如果有的话最好使用它,但问题是关于Boost,所以我认为不是。
    【解决方案3】:
    The spinlock solution provided by erenon sometimes generated crumble cout result. but the boost::mutext solution won't. So either the solution is incorrect, or my understanding of cout is wrong.
    
    #include <iostream>
    #include <thread>
    #include <atomic>
    #include <boost/thread/mutex.hpp>
    using namespace std;
    
    class spinlock {
        private:
            std::atomic<bool> lock_;
    
        public:
            spinlock() {
                lock_.store(false);
            }
    
            void lock() {
                bool unlocked = false;
                while (!lock_.compare_exchange_weak(unlocked, true));
    
            }
    
            void unlock() {
                lock_.store(false);
            }
        };
    
    
    class add_one
    {
        private:
            std::string name_;
            unsigned int& num_;
            spinlock & lock_;
            boost::mutex & mutex_;
        public:
        add_one(std::string name, unsigned int& num, spinlock &lock, boost::mutex &mutex)
        :name_(name),
        num_(num),
        lock_(lock),
        mutex_(mutex)
        {
    
        }
    
        void add_and_display()
        {
            while(true)
            {
                lock_.lock();
                //boost::lock_guard<boost::mutex> g( mutex_);
                std::cout << name_ << " " << num_ << endl;
                if(num_ == 10000000)
                {
    
                    return;
                }
                num_++;
                lock_.unlock();
    
            }
        }
    };
    
    int main()
    {
       cout << "Hello World" << endl;
       unsigned int n = 0;
       spinlock lock;
       boost::mutex mutex;
       add_one one("t1", n ,lock, mutex);
       add_one two("t2", n ,lock, mutex);
       add_one three("t3", n ,lock, mutex);
       //add_one four("t4", n ,lock, mutex);
       std::thread t1(std::bind(&add_one::add_and_display, one));
       std::thread t2(std::bind(&add_one::add_and_display, two));
       std::thread t3(std::bind(&add_one::add_and_display, three));
       //std::thread t4(std::bind(&add_one::add_and_display, four));
    
       t1.join();
       t2.join();
       t3.join();
       //t4.join();
    
       return 0;
    }
    

    【讨论】:

      【解决方案4】:

      有点离题但相关——可以使用 C++11(而不是 C++03)获得无锁保证自旋锁,而无需 boost(尽管那是问题标题)。

      使用 C++11 std::atomic_flag.

      正如 Evgeny Panasyuk 在 erenon's answer 的 cmets 中所指出的,它始终是无锁的。 std::atomic 似乎不能保证无需等待(在某些情况下是必需的)。

      也符合BasicLockable接口,std::lock_guard/std::scoped_lock/...

      class Spinlock {
        std::atomic_flag _lock = ATOMIC_FLAG_INIT;
      
       public:
        void lock() {
          while (_lock.test_and_set(std::memory_order_acquire)) continue;
        }
        void unlock() {
          _lock.clear(std::memory_order_release);
        }
      };
      

      【讨论】:

        猜你喜欢
        • 2020-03-27
        • 2021-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        • 1970-01-01
        • 2021-01-22
        • 1970-01-01
        相关资源
        最近更新 更多