Linux c++マルチスレッドプログラミングの基礎--反発ロック


1.はじめに
マルチスレッドプログラミングのブログを整理しようとしたが、C++のマルチスレッドはJavaよりも穴があいていることに気づいた.関連する範囲は本当に少し広いので、後で分けてゆっくり説明して、まずこの反発ロックについて話します.
まず反発ですが、これは何ですか?そういえばもう一枚の内容ですが、詳しくは自分で調べて、書籍『オペレーティングシステム——エッセンスと設計原理(第7版)』第5章を参考に、ここでお話ししたいと思います.
反発とは、あるプロセスが臨界領域のリソースを使用し、別のプロセスが使用できないことです.例えば、交通道路では、赤信号が青信号を止めて、赤信号の时、あなたは止まって待つ必要があります.逆に、青信号であれば、通信することができます.これは赤信号と青信号が信号量です.もし1つの道が青信号であれば、この道の車はすべて通過することができます.青信号を得られない道は待つしかありません.これが反発です.
2.反発ロックpthread_mutex_t
(1)反発ロックの作成
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

int main()
{
    pthread_mutexattr_t mut_at;
    pthread_mutexattr_init(&mut_at);

    pthread_mutex_t mut;
    mut = PTHREAD_MUTEX_INITIALIZER;

    pthread_mutex_init(&mut,&mut_at);
}

反発ロックを作成するには、PTHREAD_を直接使用する2つの方法があります.MUTEX_INITIALIZER直接初期化、PTHREAD_MUTEX_INITIALIZERの下位層での実現は以下の通りである.
# define PTHREAD_MUTEX_INITIALIZER \
  { { 0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 } } }

__PTHREAD_SPINSの最下位実装は以下の通りである.
# define __PTHREAD_SPINS             0, 0

はっきり言って、これを使って鍵の中の値をすべて0に割り当てます.
2つ目はpthread_を呼び出すmutex_init、その関数のプロトタイプは以下の通りです.
/* Initialize a mutex.  */
extern int pthread_mutex_init (pthread_mutex_t *__mutex,
			       const pthread_mutexattr_t *__mutexattr)
     __THROW __nonnull ((1));
pthread_を使用mutexattr_tのポインタはpthread_を初期化するmutex_t
2つの構造体の実装を見てみると、実はこの2つは基本的に同じですが、pthread_mutex_t比pthread_mutexattr_tは構造体を1つ増やし、内部定義は以下の通りである.
/* Data structures for mutex handling.  The structure of the attribute
   type is not exposed on purpose.  */
typedef union
{
  struct __pthread_mutex_s
  {
    int __lock;
    unsigned int __count;
    int __owner;
#ifdef __x86_64__
    unsigned int __nusers;
#endif
    /* KIND must stay at this position in the structure to maintain
       binary compatibility.  */
    int __kind;
#ifdef __x86_64__
    short __spins;
    short __elision;
    __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV	1
/* Mutex __spins initializer used by PTHREAD_MUTEX_INITIALIZER.  */
# define __PTHREAD_SPINS             0, 0
#else
    unsigned int __nusers;
    __extension__ union
    {
      struct
      {
	short __espins;
	short __elision;
# define __spins __elision_data.__espins
# define __elision __elision_data.__elision
# define __PTHREAD_SPINS         { 0, 0 }
      } __elision_data;
      __pthread_slist_t __list;
    };
#endif
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;
} pthread_mutex_t;

typedef union
{
  char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
  int __align;
} pthread_mutexattr_t;

この構造体の内部には、スレッドを使用するオブジェクト識別子、オブジェクトが待機するキュー、ロックのプロパティなどのデータ情報が定義されています.
△実は私もこれらのものが何なのか悩んでいます.鍵をかけるのに何が必要なのか、なぜこんなに多くのものが必要なのかを考えてみましょう.これは私が深く理解して説明しています.
(2)ロックの作用範囲:
反発ロックの主な作用範囲は2つあります.
1つは、PTHREAD_に対応する同じプロセス内のスレッドの同期です.PROCESS_PRIVATE
もう1つは、PTHREAD_に対応する異なるプロセス内のスレッドの同期を使用することです.PROCESS_SHAPE
この変数の値を設定するにはどうすればいいですか?次のコードを参照してください.
int main()
{
    pthread_mutexattr_t mut_at;
    pthread_mutex_t mut;
    pthread_mutexattr_init(&mut_at);
    //  mut_at          PTHREAD_PROCESS_SHARED
    pthread_mutexattr_setpshared(&mut_at,PTHREAD_PROCESS_SHARED);
    //  mut_at          PTHREAD_PROCESS_PRIVATE
    pthread_mutexattr_setpshared(&mut_at,PTHREAD_PROCESS_PRIVATE);
    pthread_mutex_init(&mut,&mut_at);
}

このプロパティを取得するにはsetをgetに変更する必要があります.次は対応する関数のプロトタイプです.
/* Get the process-shared flag of the mutex attribute ATTR.  */
extern int pthread_mutexattr_getpshared (const pthread_mutexattr_t *
					 __restrict __attr,
					 int *__restrict __pshared)
     __THROW __nonnull ((1, 2));

/* Set the process-shared flag of the mutex attribute ATTR.  */
extern int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr,
					 int __pshared)
     __THROW __nonnull ((1));

どうやって作ったのか分からないので、原型の中のパラメータを見てもいいです.
(3)属性:
PTHREAD_MUTEX_TIMED_NP:この属性はデフォルト属性で、デフォルト値です.1つのスレッドがロックされると、残りのロックを要求するスレッドは待機キューを形成し、ロックが解除されると優先度にロックを取得します.
PTHREAD_MUTEX_RECURSIVE_NP:ネストされた所で、同じスレッドで同じロックを複数回取得し、unlockで複数回ロックを解除できます.異なるスレッドリクエストの場合、スレッドがロック解除された後に競合する必要があります.
PTHREAD_MUTEX_ERRORCHECK_NP:エラーチェックロック.機能は同じ1つ目ですが、このロックは同じスレッドが同じロックを要求したときにEDEADLKに戻ります.他の同じ1つ目は、同じロックを2回以上取得していないことを保証します.
PTHREAD_MUTEX_ADAPTIVE_NP:適応ロック.
以上の4つを属性として、これらの属性をどのように設定するかを紹介します.次の関数のサポートが必要です.
/* Return in *KIND the mutex kind attribute in *ATTR.  */
extern int pthread_mutexattr_gettype (const pthread_mutexattr_t *__restrict
				      __attr, int *__restrict __kind)
     __THROW __nonnull ((1, 2));

/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_NORMAL,
   PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, or
   PTHREAD_MUTEX_DEFAULT).  */
extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind)
     __THROW __nonnull ((1));

ロックを取得するプロパティと、ロックを設定するプロパティです.具体的には次のように使用します.
pthread_mutexattr_settype(&mut_at,PTHREAD_MUTEX_TIMED_NP);
pthread_mutex_init(&mut,&mut_at);

(4)ロックの操作:
/* Try locking a mutex.  */
extern int pthread_mutex_trylock (pthread_mutex_t *__mutex)
     __THROWNL __nonnull ((1));

/* Lock a mutex.  */
extern int pthread_mutex_lock (pthread_mutex_t *__mutex)
     __THROWNL __nonnull ((1));

/* Unlock a mutex.  */
extern int pthread_mutex_unlock (pthread_mutex_t *__mutex)
     __THROWNL __nonnull ((1));

この4つの関数について説明します.
pthread_mutex_lock:ロックをかけます.ロックがすでに占有されている場合、スレッドはキューに追加されます.
pthread_mutex_trylock:ロックを追加しようとします.ロックが占有されている場合、スレッドはキューに参加せず、エラーを返します.
pthread_mutex_unlock:ロックを解除します.
(5)具体例:
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

int tf[5];

void* print(void* i)
{
    pthread_mutex_lock(&mut);
    for(int j=0;j<5;j++)
        cout << i << " " << j << endl;
    pthread_mutex_unlock(&mut);
}

int main()
{
    pthread_t td[5];
    for(int i=0;i<5;i++)
        tf[i] = i;
    for(int i=0;i<5;i++)
        pthread_create(&td[i],NULL,print,(void *)&tf[i]);
    for(int i=0;i<5;i++)
        pthread_join(td[i],NULL);
    pthread_mutex_destroy(&mut);
}

これにより、ロックを解除するには、各スレッドが終了するまで実行する必要があり、他のスレッドがアクセスできるようになります.実行結果は次のとおりです.
0x602194 0
0x602194 1
0x602194 2
0x602194 3
0x602194 4
0x602190 0
0x602190 1
0x602190 2
0x602190 3
0x602190 4
0x6021a0 0
0x6021a0 1
0x6021a0 2
0x6021a0 3
0x6021a0 4
0x60219c 0
0x60219c 1
0x60219c 2
0x60219c 3
0x60219c 4
0x602198 0
0x602198 1
0x602198 2
0x602198 3
0x602198 4

今この反発ロックについて話しますが、間違いがあったらよろしくお願いします.
参照リンク:
https://blog.csdn.net/zmxiangde_88/article/details/7998458