同時プログラミング入門(二):Boostの反発量と条件変数に対するパッケージを分析し、生産者の消費者問題を実現する.

8084 ワード

前の記事「同時プログラミング実戦:POSIXは反発量と条件変数を使用して生産者/消費者問題を実現する」を参照してください.もちろん読まなくても、この文章の読書には影響しません.
Boostの反発量、条件変数はよくパッケージされているので、「オリジナル」のPOSIX mutex、condition variablesより使いやすいです.次にboost関連ソースコードを分析してboost linuxがpthread_にどのように対応しているかを見てみましょうmutex_tとpthread_cond_tによるパッケージ.
まずcondition_を見てみましょうvariable_anyの具体的な実装、コードパス:/boost/thread/pthread/condition_variable.hpp
class condition_variable_any
{
    pthread_mutex_t internal_mutex;
    pthread_cond_t cond;

    condition_variable_any(condition_variable_any&);
    condition_variable_any& operator=(condition_variable_any&);

public:
    condition_variable_any()
    {
        int const res=pthread_mutex_init(&internal_mutex,NULL);
        if(res)
        {
            boost::throw_exception(thread_resource_error());
        }
        int const res2=pthread_cond_init(&cond,NULL);
        if(res2)
        {
            BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex));
            boost::throw_exception(thread_resource_error());
        }
    }
    ~condition_variable_any()
    {
        BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex));
        BOOST_VERIFY(!pthread_cond_destroy(&cond));
    }
 
condition_variable_anyのコンストラクション関数は内部で使用されるmutexとcondの初期化であり,対応し,コンストラクション関数はこれらのリソースの回収である.
BOOST_VERIFYの実現:
#undef BOOST_VERIFY  
#if defined(BOOST_DISABLE_ASSERTS) || ( !defined(BOOST_ENABLE_ASSERT_HANDLER) && defined(NDEBUG) )  
//  ,expr 。  
#define BOOST_VERIFY(expr) ((void)(expr))  
#else  
#define BOOST_VERIFY(expr) BOOST_ASSERT(expr)  
#endif  
それゆえassertがRelease版で最適化されたのとは異なり、BOOST_を安心して使用することができます.VERITYなので、assertのside effectを心配することなく、式は必ず評価されます.
次にcondition_を見てみましょうvariable_anyのコア実装:wait
   template
    void wait(lock_type& m)
    {
        int res=0;
        {
            thread_cv_detail::lock_on_exit guard;
            detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
            guard.activate(m);
            res=pthread_cond_wait(&cond,&internal_mutex);
            this_thread::interruption_point();
        }
        if(res)
        {
            boost::throw_exception(condition_error());
        }
    }
まずlock_を見てみましょうon_exit:
namespace thread_cv_detail
{
    template
    struct lock_on_exit
    {
        MutexType* m;

        lock_on_exit():
            m(0)
        {}

        void activate(MutexType& m_)
        {
            m_.unlock();
            m=&m_;
        }
        ~lock_on_exit()
        {
            if(m)
            {
                m->lock();
            }
       }
    };
}
コードは簡単で、activateを呼び出すと入力されたlockがロック解除され、この変数のライフサイクル終了時にguardのlockがロックされます.
次のdetail::interruption_checker check_for_interruption(&internal_mutex,&cond);どういう意味ですか.From/boost/thread/pthread/thread_data.hpp
class interruption_checker
{
    thread_data_base* const thread_info;
    pthread_mutex_t* m;
    bool set;

    void check_for_interruption()
    {
        if(thread_info->interrupt_requested)
        {
            thread_info->interrupt_requested=false;
            throw thread_interrupted();
        }
    }

    void operator=(interruption_checker&);
public:
    explicit interruption_checker(pthread_mutex_t* cond_mutex,pthread_cond_t* cond):
        thread_info(detail::get_current_thread_data()),m(cond_mutex),
        set(thread_info && thread_info->interrupt_enabled)
    {
        if(set)
        {
            lock_guard guard(thread_info->data_mutex);
            check_for_interruption();
            thread_info->cond_mutex=cond_mutex;
            thread_info->current_cond=cond;
            BOOST_VERIFY(!pthread_mutex_lock(m));
        }
        else
        {
            BOOST_VERIFY(!pthread_mutex_lock(m));
        }
    }
    ~interruption_checker()
    {
        if(set)
        {
            BOOST_VERIFY(!pthread_mutex_unlock(m));
            lock_guard guard(thread_info->data_mutex);
            thread_info->cond_mutex=NULL;
            thread_info->current_cond=NULL;
        }
        else
        {
            BOOST_VERIFY(!pthread_mutex_unlock(m));
        }
    }
コードの前には、隠されていません.その言葉はこの時にinterruptがあればinterruptにしましょう.そうでなければ、lockが伝えたmutexもres=pthread_のためです.cond_wait(&cond,&internal_mutex);準備をする.
スレッドのブレークポイントについては、「【Boost】boostライブラリのthreadマルチスレッドの詳細5-スレッドのブレークについて話す」に移動します.
boost::mutexについては、同じ方法でboostの実装を解読することができ、condition variableよりもmutexの実装が直感的である.コードパス:/boost/thread/pthread/mutex.hpp.
namespace boost
{
    class mutex
    {
    private:
        mutex(mutex const&);
        mutex& operator=(mutex const&);
        pthread_mutex_t m;
    public:
        mutex()
        {
            int const res=pthread_mutex_init(&m,NULL);
            if(res)
            {
                boost::throw_exception(thread_resource_error());
            }
        }
        ~mutex()
        {
            BOOST_VERIFY(!pthread_mutex_destroy(&m));
        }

        void lock()
        {
            int const res=pthread_mutex_lock(&m);
            if(res)
            {
                boost::throw_exception(lock_error(res));
            }
        }

        void unlock()
        {
            BOOST_VERIFY(!pthread_mutex_unlock(&m));
        }

        bool try_lock()
        {
            int const res=pthread_mutex_trylock(&m);
            if(res && (res!=EBUSY))
            {
                boost::throw_exception(lock_error(res));
            }

            return !res;
        }

        typedef pthread_mutex_t* native_handle_type;
        native_handle_type native_handle()
        {
            return &m;
        }

        typedef unique_lock scoped_lock;
        typedef detail::try_lock_wrapper scoped_try_lock;
    }; 
}

boostはpthread_に対してmutex_tとpthread_cond_tのパッケージは,開発者が使用するリソースの安全かつ効率的な管理を容易にする.もちろん、異なる会社でも、似たようなパッケージがあるかもしれませんが、boostのソースコードを学ぶことは、私たちの理解を深めることができます.特定の場合、boostのパッケージ方法を学び、日常的な開発を簡素化することもできます.
最後に、簡単な生産者、消費者のboostの実現を奉じ、前述の「同時プログラミング実戦:POSIXは反発量と条件変数を用いて生産者/消費者問題を実現する」に比べて、boostはmutexとcondition variableの使用を簡略化していることがわかる.次のコードは、Boostライブラリの完全開発ガイドから引用されています.
#include 
#include 
using std::stack;
using std::cout;
class buffer
{
private:
    boost::mutex mu; //  
    boost::condition_variable_any cond_put; //  
    boost::condition_variable_any cond_get; //  

    stack stk;
    int un_read;
    int capacity;

    bool is_full()
    {
        return un_read == capacity;
    }
    bool is_empty()
    {
        return 0 == un_read;
    }

public:
    buffer(size_t capacity) : un_read(0), capacity(capacity)
    {}
 void put(int x)
 {

 boost::mutex::scoped_lock lock(mu); //  

 while (is_full())
 {
 cout << "full waiting..." << endl;
 cond_put.wait(mu); // line:51
 }
 stk.push(x);
 ++un_read;

 cond_get.notify_one();
 }
 void get(int *x)
 {
 boost::mutex::scoped_lock lock(mu); //  

 while (is_empty())
 {
 cout << "empty waiting..." << endl;
 cond_get.wait(mu);
 }
 *x = stk.top();
 stk.pop();
 --un_read;

 cond_put.notify_one(); //   51line 
 }
};

buffer buf(5); 

void producer(int n)
{
 for (int i = 0; i < n; ++i)
 {
 cout << "put : " << i << endl;
 buf.put(i);
 }
}

void consumer(int n)
{
 int x;
 for (int i = 0; i < n; ++i)
 {
 buf.get(&x);
 cout << "get : " << x << endl;
 }
}

int main()
{
 boost::thread t1(producer, 20);
 boost::thread t2(consumer, 10);
 boost::thread t3(consumer, 10);

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

 return 0;
}
最後に一言、condition_variable_any == condition, from/boost/thread/condition.hpp
namespace boost
{
    typedef condition_variable_any condition;
}