muduライブラリ学習編-mutexクラスパッケージ学習


muduoライブラリは高同時linuxネットワークプログラミングライブラリであり、mutexクラスのパッケージがあるに違いない.マルチスレッドのプログラミングでは、スレッド間でグローバル変数が操作されることが多いので、同期のメカニズムが必要だ.私の前のブログcondtionは一種だ.もちろんconditonは一般的にmutexと組み合わせて使用されている.次にmuduoライブラリのmutexへのパッケージを見てみましょう
mutex.h
namespace muduo
{

// Use as data member of a class, eg.
//
// class Foo
// {
//  public:
//   int size() const;
//
//  private:
//   mutable MutexLock mutex_;
//   std::vector data_ GUARDED_BY(mutex_);
// };
class CAPABILITY("mutex") MutexLock : noncopyable
{
 public:
  MutexLock()
    : holder_(0)
  {
    MCHECK(pthread_mutex_init(&mutex_, NULL));
  }

  ~MutexLock()
  {
    assert(holder_ == 0);
    MCHECK(pthread_mutex_destroy(&mutex_));
  }

  // must be called when locked, i.e. for assertion
  bool isLockedByThisThread() const
  {
    return holder_ == CurrentThread::tid();
  }

  void assertLocked() const ASSERT_CAPABILITY(this)
  {
    assert(isLockedByThisThread());
  }

  // internal usage

  void lock() ACQUIRE()
  {
    MCHECK(pthread_mutex_lock(&mutex_));
    assignHolder();
  }

  void unlock() RELEASE()
  {
    unassignHolder();
    MCHECK(pthread_mutex_unlock(&mutex_));
  }

  pthread_mutex_t* getPthreadMutex() /* non-const */
  {
    return &mutex_;
  }

 private:
  friend class Condition;

  class UnassignGuard : noncopyable
  {
   public:
    explicit UnassignGuard(MutexLock& owner)
      : owner_(owner)
    {
      owner_.unassignHolder();
    }

    ~UnassignGuard()
    {
      owner_.assignHolder();
    }

   private:
    MutexLock& owner_;
  };

  void unassignHolder()
  {
    holder_ = 0;
  }

  void assignHolder()
  {
    holder_ = CurrentThread::tid();
  }

  pthread_mutex_t mutex_;
  pid_t holder_;
};

// Use as a stack variable, eg.
// int Foo::size() const
// {
//   MutexLockGuard lock(mutex_);
//   return data_.size();
// }
class SCOPED_CAPABILITY MutexLockGuard : noncopyable
{
 public:
  explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
    : mutex_(mutex)
  {
    mutex_.lock();
  }

  ~MutexLockGuard() RELEASE()
  {
    mutex_.unlock();
  }

 private:

  MutexLock& mutex_;
};

}  // namespace muduo

// Prevent misuse like:
// MutexLockGuard(mutex_);
// A tempory object doesn't hold the lock for long!
#define MutexLockGuard(x) error "Missing guard object name"

#endif  // MUDUO_BASE_MUTEX_H


mutex.hにはmutexのパッケージに対するMutexlockクラスと,Mutexlockパッケージに基づくMutexLockGuardがある.
  • Mutexlockクラスmutexlockクラスには2つのメンバー変数があります:pthread_mutex_t mutex_ linux反発ロックpid_t holder_; プロセスidタイプmutexlockの構造関数はmutexの初期化とプロセスidの初期化を実現し、デフォルト値は0
  • である.
  • MutexLockGuard類MutexLockGuard類の実現思想はスマートポインタに似ており、MutexLockGuardを使用することで、マルチスレッドプログラミング時に開発者がロックの解放を忘れないように実現できる思想は、相互反発ロックの制御をオペレーティングシステムに渡し、1つのスタック変数を通じて1つのグローバル変数を管理し、スタック変数が役割ドメインから終了すると、システムはこのスタック領域の構造関数を自動的に呼び出してロックを解放する目的を達成し、スマートポインタは1つのポインタの解放問題をプログラマーからシステムに移行することであるが、ポインタにしか肝心な点は、MutexLockGuardクラスのメンバー変数が&クラス型の変数でなければならないことであり、次に構造関数でmutexlockのlock関数を呼び出すことである.構造関数はmutexlockのunlock関数を呼び出します.

  • 広がる
    c++11ではmutex、c++11ではmutexもカプセル化されているが、使用方法とmuduoライブラリの差は多くない.また、c++11ではMutexLockGuardのような実装もある.以下、c++11ではmutexカプセル化に基づくlock_を紹介するguardとunique_lock;
    lock_guard
    #include 
    #include 
    #include 
    #include 
    #include 
    
    std::mutex my_lock;
    
    void add(int &num, int &sum){
        while(true){
            std::lock_guard<:mutex> lock(my_lock);  
            if (num < 100){ //    
                num += 1;
                sum += num;
            }   
            else {  //    
                break;
            }
            //sleep(100);
        }   
    }
    
    int main(){
        int sum = 0;
        int num = 0;
        std::vector<:thread> ver;   //     vector
        for(int i = 0; i < 20; ++i){
            std::thread t = std::thread(add, std::ref(num), std::ref(sum));
            ver.emplace_back(std::move(t)); //    
        }   
    
        std::for_each(ver.begin(), ver.end(), 			      std::mem_fn(&std::thread::join)); //join
        std::cout << sum << std::endl;
    }
    

    同じ上のコードはmuduoライブラリのMutexLockGuardを使用するのと同じです.muduoライブラリのMutexLockGuardとc++11のlock_guardには、スレッド内で完全な局所変数を操作した後、まだ長い処理ロジックがあり、終了に時間がかかる可能性があります.例えば、私は上のsleep(100)にいます.これにより、他のスレッドがブロックされ続け、効率が低下するため、上の2つのロックは短い局所ロックに適しています.
    unique_lock
    #include        // std::cout
    #include          // std::thread
    #include           // std::mutex, std::unique_lock
    #include 
    
    std::mutex mtx;           // mutex for critical section
    void add(int &num, int &sum) {
    	while (true) {
    		std::unique_lock<:mutex> lock(my_lock);
    		if (num < 50) { //    
    			num += 1;
    			sum += num;
    		}
    		else {
    			break;
    		}
    		lock.unlock(); //   
    
    		Sleep(10);
    
    		lock.lock();//    ,            
    		sum += num;
    	}
    }
    
    int main() {
    	int sum = 0;
    	int num = 0;
    	std::vector<:thread> ver;   //     vector
    	for (int i = 0; i < 20; ++i) {
    		std::thread t = std::thread(add, std::ref(num), std::ref(sum));
    		ver.emplace_back(std::move(t)); //    
    	}
    
    	std::for_each(ver.begin(), ver.end(), std::mem_fn(&std::thread::join)); //join
    	std::cout << sum << std::endl;
    	return 0;
    }