(for Internet Explorer)
プロセス制御
プロセスID → プロセスハンドル : OpenProcess 〜 CloseHandle
参考
DDE(高負荷時、遅い)
(cache)
→ DDE.HTM
IPC
軽いが通知のみでデータは送れない
プロセス間通信は、データのすべてをコピーする必要があります。
スレッド間通信では、ポインタで渡すことができます。
イベントは、OSでグローバルな(1台のマシンの全体で識別する)名前を持つことができます。
イベント名は、多くの場合、プログラムで固定されているか、設定ファイルに入っています。

イベントは、通知だけで、データは送れません。

1つのイベントで、複数のスレッドが待つことはできません。(予想)
SetEvent( event );
WaitForSingleObject( event, INFINITE );
ResetEvent( event );
event = CreateEvent( NULL, FALSE, FALSE, "MyAppEvent" );
イベントは、別のプロセスで待っているスレッドをトリガーします。
HANDLE  event
送信側
受信側
OS
イベント
HANDLE  event
参照
参照
イベント名を指定して、イベントを作成します。
初期化するときに、送信側、受信側とも同じ名前を指定して、CreateEvent を呼び出します。
受信側は、スレッドのループに入った直後で、待ち状態に入るようにします。 WaitForSingleObject
を呼び出したら、イベントが発生するまで、関数から戻りません。
次の WaitForSingleObject で誤動作しないように、Wait する側で ResetEvent します。
送信側は、受信側の(別のプロセスで待っている)スレッドに対して、SetEvent でトリガーします。
受信側は、WaitForSingleObject から戻るので、応答処理を実行します。
処理が済んだら、再び WaitForSingleObject 関数を呼び出します。
受信を終了するときは、イベントを後始末します。
→ スレッド
CloseHandle( event );
アプリケーションを終了するときなど、送信側が送信を終了するときや、受信側が受信を
終了するときは、CloseHandle を呼び出します。
送信側、受信側とも Close したら、OS からイベントが削除されます。
送信側
受信側
イベント
SetEvent (イベント発生)
WaitForSingleObject 呼出し
WaitForSingleObject から戻る
(イベント受信)
(待つ)
Win32
プロセスA
プロセスB
待ち解除
通知
第2引数を TRUE にしたとき、WaitForSingleObject から抜けても自動的に
リセットされないことがあるようです。
セマフォを作成します
カウント値を1つ減らします
カウント値を増やします
セマフォを削除します
sema = CreateSemaphore( NULL, 0, 1, "sema" );
ReleaseSemaphore( sema, 1, NULL );  /* 通知 */
CloseHandle( sema );
sema = CreateSemaphore( NULL, 0, 1, "sema" );
for (;;) {
  WaitForSingleObject( sema, INFINITE );
}
CloseHandle( sema );
通知元
通知先
→ SEMA.HTM
HANDLE  CreateSemaphore( SECURITY_ATTRIBUTES* secur, LONG init, LONG max, TSTR* name );
【引数】
secur
セキュリティ関係(使わないなら NULL)
init
初期カウント値、0 なら ReleaseSemaphore から始める
セマフォを作成します。
セマフォは、カウント値(残り資源数)を減らそうとしたとき、すでに 0 なら待つものです。
max
最大カウント値
name
セマフォの名前
返り値
セマフォのハンドル
[ 親: セマフォ ]
BOOL  ReleaseSemaphore( HANDLE sema, LONG n,  LONG* nPrev );
セマフォのカウントをプラスします。(資源を返します)
【引数】
sema
セマフォのハンドル
n
プラスする値
nPrev
プラスする前の値(デバッグ用)、参照しない=NULL
返り値
成功したかどうか
[ 親: セマフォ ]
複数のプロセスの間で、片方向通信ができます。
受信側は、1つのメールスロットで、複数のプロセスを相手にできます。
Write したとき、相手の受信を待たずに、次に進めます。
発信プロセス
発信プロセス
受信プロセス
メールスロット
 メールスロットを作成します
メールスロットを開きます。ただし、FILE_SHARE_READ が必要です
受信プロセス用:
受信するまで待って、受信したらデータを取得します
送信プロセス用:
データを送信します
API
WinNT3.1, Win95以降
受信プロセスで作成したメールスロットと同じ名前のものは、異なるプロセス、
または、異なるユーザでも作成できません。
HANDLE  CreateMailslot( TSTR* name,  DWORD maxMessageSize, DWORD readTimeout,
                        SECURITY_ATTRIBUTES* secur );
メールスロット名( \\.\mailslot\name )、NULL不可
name
【引数】
1 つのメッセージの最大長、0=任意長
maxMessageSize
readTimeout
リードするときのタイムアウト(ミリ秒)か MAILSLOT_WAIT_FOREVER
secur
セキュリティ関係(使わないなら NULL)
返り値
メールスロットのハンドルか、INVALID_HANDLE_VALUE
同じプロセスで使うときも、メールスロット名は省略できません。
同じプロセス内で通信するときでも、送信側は、CreateFile する必要があります。
受信用に メールスロットを作成します
受信プロセスで作成したメールスロットと同じ名前のものは、異なるプロセス、
または、異なるユーザでも作成できません。
#include <windows.h>
#define  error() {}


int main(int argc, char* argv[])
{
  char   read_data[256];
  DWORD  read_size;
  BOOL   b;
  HANDLE slot;

  slot = CreateMailslot( "\\\\.\\mailslot\\test", 0, MAILSLOT_WAIT_FOREVER, NULL );
  if ( slot == INVALID_HANDLE_VALUE )  error();

  for (;;) {

    b = ReadFile( slot, read_data, 4, &read_size, NULL );
    if ( ! b )  error();

//printf( "%s\n", read_data );
    if ( memcmp( read_data, "end", 4 ) == 0 )  break;
  }

  CloseHandle( slot );

  return 0;
}
#include <windows.h>
#define  error() {}

int  main( int argc, char* argv[] )
{
  char   write_data[256];
  DWORD  write_size;
  BOOL   b;
  HANDLE slot;

  slot = CreateFile( "\\\\.\\mailslot\\test", GENERIC_WRITE,
           FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  if ( slot == INVALID_HANDLE_VALUE )  error();

  strcpy( write_data, "abc" );
  b = WriteFile( slot, write_data, 4, &write_size, NULL );
  if ( ! b )  error();

  strcpy( write_data, "end" );
  b = WriteFile( slot, write_data, 4, &write_size, NULL );
  if ( ! b )  error();

  CloseHandle( slot );

  return 0;
}
WinCE 4 以降
\\.\pipe\pipe_name のような名前を指定して、プロセス間通信します。
プロセスの間で、1対1の双方向通信(発信と応答)ができます。
サーバ側で、処理をするまで戻りません。
プロセス(クライアント)
プロセス(クライアント)
プロセス(サーバ)
パイプA
パイプB
インパーソネーション: 通信相手(送信側)のアクセス権限で実行すること
サーバー側で、クライアントがパイプに接続してくるのを待ちます
MSDN(Web) へのリンク
サーバー側で、名前付きパイプを作成します
クライアント側で、パイプを開き、送受信し、閉じます。注意→下記
受信します。 CreateNamedPipe で PIPE_WAIT を指定したときに、
通信相手と接続状態、かつ受信データが無ければ待ちます。
名前つきパイプのハンドルを閉じます
送信します。
(他にもあります)
WinNT, 2000, Xp
CallNamedPipe の注意点
クライアント側で CallNamedPipe を使う場合、サーバ側の CreateNamePipe で、
PIPE_ACCESS_DUPLEX と PIPE_TYPE_MESSAGE を指定する必要があります。
CallNamedPipe は、送信と受信の両方を行うので、サーバが ReadFile だけでなく WriteFile
しなければ、CallNamedPipe関数から戻りません。
CallNamedPipe は。関数から抜ける直前に名前付きパイプを閉じるのですが、
閉じられたことをサーバ側の名前付きパイプは認識すると、これ以上、名前付きパイプを
使わないと判断し、CloseHandle 以外の処理を受け付けなくなります。
CloseHandle してから、再度 CreateNamePipe を行って、次のメッセージに備えます。
サーバ側の CloseHandle がクライアントより先に実行されると、サーバ側の CloseHandle が
失敗しますが、エラーを返しません。 先にクライアントが CloseHandle するように同期処理が
必要です。
サーバが CreateNamePipe するより先にクライアント側で CallNamedPipe すると、ウェイト
しないで、すぐに戻ってしまいます。待つためには、同期処理が必要です。











#include <windows.h>
#define  error() {}

int  main( int argc, char* argv[] )
{
  HANDLE pipe;
  HANDLE sema;
  char   read_data[256];
  char   write_data[256];
  DWORD  size;
  BOOL   b;
  bool   bExit = false;

  sema = CreateSemaphore( NULL, 0, 1, "MyPipeSema" );

  while ( ! bExit ) {

    pipe = CreateNamedPipe( "\\\\.\\pipe\\mypipe",
       PIPE_ACCESS_DUPLEX,  PIPE_TYPE_MESSAGE | PIPE_WAIT,
       1, 1024, 1024, 10000, NULL );
    if ( pipe == INVALID_HANDLE_VALUE )  error();

    ReleaseSemaphore( sema, 1, NULL );

    b = ConnectNamedPipe( pipe, NULL );
    // すでに接続されているときは、エラー(b=1)になりますが無視できます。

    b = ReadFile( pipe, read_data, sizeof(read_data), &size, NULL );
    if ( ! b )  error();
//printf( "(%d) %s\n", size, read_data );

    write_data[0] = 0x00;
    b = WriteFile( pipe, write_data, 1, &size, NULL );
    if ( ! b )  error();

    WaitForSingleObject( sema, INFINITE );

    b = CloseHandle( pipe );
    if ( ! b ) error();
  }

  CloseHandle( sema );
  return 0;
#include <windows.h>
#define  error() {}

int  main( int argc, char* argv[] )
{
  char   write_data[256];
  char   read_data[256];
  DWORD  read_size;
  BOOL   b;
  HANDLE sema;

  sprintf( write_data, "abc%d", i );

  sema = CreateSemaphore( NULL, 0, 1, "MyPipeSema" );
  WaitForSingleObject( sema, INFINITE );

  b = CallNamedPipe( "\\\\.\\pipe\\mypipe",
         write_data, strlen(write_data) + 1,
         read_data, sizeof(read_data), &read_size,
         NMPWAIT_USE_DEFAULT_WAIT );
  if ( ! b )  error();

  ReleaseSemaphore( sema, 1, NULL );
  CloseHandle( sema );

  return 0;
}
サーバ
クライアント
CreateNamedPipe
ConnectNamedPipe
CallNamedPipe
開く
ライト
リード
閉じる
ReadFile
WriteFile
CloseHandle
パイプ
待ち
リターン
サーバを起動させた状態で、クライアントを起動して、データを送受信します。
COPYDATASTRUCT  d;

d.dwData = 12;     // 数値データ
d.lpData = "abc";  // バイナリデータの先頭アドレス
d.cbData = size;   // バイナリデータのサイズ

SendMessage( (HWND)target_hWnd, WM_COPYDATA, (WPARAM)m_hWnd, (LPARAM)&d );
送信側
BEGIN_MESSAGE_MAP( , )
    :
  ON_WM_COPYDATA()
END_MESSAGE_MAP()
BOOL  〜::OnCopyData( CWnd* sender, COPYDATASTRUCT* p )
{
  if ( p->dwData == ... ) { }
}
受信側
ウィンドウメッセージを使って、任意の数値と、任意サイズのバイナリを、転送します。
転送先のウィンドウハンドルの値がわかる必要があります。
Win95以降
プロセスもスレッドも、他のプロセスやスレッドと平行して動作します。
プロセスは、他のプロセスの変数を直接参照することはできません。
マルチコアやハイパースレッドのCPUでは、他のスレッドと同時に動作します。
書きかけ
void CDlg::OnOK()
{
  ASSERT( m_Thread == NULL );

  m_OK.EnableWindow( FALSE );
  m_Cancel.SetWindowText( "中止" );
  m_Thread = CreateThread(NULL, 0, CDlg::OKThread, this, 0, NULL );
}


DWORD WINAPI  CDlg::OKThread( LPVOID lpV )
{
  CDlg*  m = (CDlg*)lpV;
  CApp*  app = (CApp*)AfxGetApp();

  c_try {

    /* メイン処理 */

  }
  c_catch ( Errors_Msg*, msg ) {
    m->MessageBox( msg->orgMsg );
  }
  c_finish {
    m->PostMessage( WM_COMMAND, CDlg_OnOK_Finished );
  } c_end_finish;

  ExitThread( CMaster2Dlg_OnOKFinished );
  return  CDlg_OnOK_Finished;
}


void  CDlg::OnOK_Finished()
{
  m_OK.EnableWindow( TRUE );
  m_Cancel.SetWindowText( "閉じる" );
  CloseHandle( m_Thread );
  m_Thread != NULL;
}
BOOL CMaster2Dlg::OnInitDialog()
{
  m_Thread = NULL;
}

BOOL CDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
  if ( wParam == CDlg_OnOKFinished )  OnOKFinished();

  return CDialog::OnCommand(wParam, lParam);
}

void CMaster2Dlg::OnCancel()
{
  if ( m_Thread != NULL ) {
    /* ワーカースレッドへ中止を通知 */  // 中止の際 PostMsg される

    WaitForSingleObject( m_Thread );
  }
  else {
    OnCancel();
  }
}
#define  CDlg_OnOK_Finished  (WM_APP+1)

class  CDlg {
    HANDLE   m_Thread;

    static DWORD WINAPI OKThread( LPVOID lpV );
    void  OnOKFinished();
}
void  main()
{
  HANDLE  Samp_go_th = CreateThread( NULL, 0, Samp_go_thread, m, 0, NULL );
  BOOL   b;
  DWORD  r;

  r = WaitForSingleObject( Samp_go_th, INFINITE );  // 完了待ち
  b = GetExitCodeThread( Samp_go_th, &r );  // 終了コードを取得
  b = CloseHandle( Samp_go_th );
}

DWORD WINAPI  Samp_go_thread( void* mm )
{
  Samp*  m = (Samp*) mm;

  SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_LOWEST );

  return  0;
}
HANDLE  CreateThread( SECURITY_ATTRIBUTES* secur,  DWORD stack_size,
                      THREAD_START_ROUTINE*  thread_f,  void* param,
                      DWORD flags,  DWORD* thread_id );
初期のスタックサイズ
stack_size
【引数】
secur
セキュリティ関係(使わないなら NULL)
thread_f
スレッド関数
param
thread_f の第1引数に渡す値
flags
thread_id
返り値
スレッドのハンドル
とりあえず NULL
とりあえず 0
スレッドを作成します。
DWORD WINAPI  THREAD_START_ROUTINE( void* param );
【引数】
param
返り値
CreateThread の第4引数(param)
スレッド関数の型です。
に、この型の関数アドレスを指定します。
に渡す値
BOOL  GetExitCodeThread( HANDLE thread, DWORD* exit_code );
スレッドの終了コードを取得します。
ExitThread または TerminateThread 関数で指定した終了コード。
スレッドが終了していないときは、exit_code == STILL_ACTIVE になります。
HANDLE  GetCurrentThread();
現在実行しているスレッドのハンドルを返します。
このハンドルは、同じプロセスの中しか使えません。
BOOL  SetThreadPriority( HANDLE thread, int priority );
デフォルトの優先順位は、THREAD_PRIORITY_NORMAL です。
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_NORMAL
プロセスの優先順位クラスによる
THREAD_PRIORITY_IDLE
THREAD_PRIORITY_TIME_CRITICAL
std
std + 1
std + 2
優先順位値(大きい方が優先)
priority 引数
15 or 31
1 or 16
std - 1
std - 2
プロセスの優先順位クラスによる
std = スレッド標準の相対優先順位値
thread 引数は、
などで取得したスレッドのハンドルです。
スレッドの優先順位を設定します。
AfxBeginThread
スレッドを生成します
CWinThread クラス
現在実行中のスレッドを終了します
AfxEndThread
(関数からリターンしても終了できます)
UINT  func_thread( void* param );
スレッド関数:
参考
CWinThread*  th = AfxBeginThread( func_thread, param, THREAD_PRIORITY_NORMAL,
   0, CREATE_SUSPENDED, NULL );
th->m_bAutoDelete = FALSE;
th->ResumeThread();
スレッド生成時:
CWinThread は終了すると自分を delete するため、タイミングによっては WaitForSingleObject
を呼び出す前に delete されて m_hThread にはアクセスできなくなってしまいます。
それを避けるため、スレッドを生成するときに、自分を delete しないように設定します。
スレッドの終了を待つ:
WaitForSingleObject( th->m_hThread, INFINITE );
delete th;
スレッドを生成します。
他のスレッドの終了を待ちます。 同時にスレッドの返り値を取得します。
自分自身のスレッドを終了します。 スレッド関数の中から実行します。
現在実行中のスレッドID を返します。
スレッドID を格納する変数の型です。
スレッドID が同じかどうか判定します。 同じなら非0 です。
サンプル
#include  <pthread.h>

#define  error()

typedef  void* (*pthread_f)(void *);
int  sub_thread( void* p );


int  main()
{
  pthread_t  sub_thread_obj;
  int  r;
  int  ret;
  void*  ptr;

  r = pthread_create( &sub_thread_obj, NULL, (pthread_f)sub_thread, ptr );  if ( r != 0 )  error();

  /* メインスレッドの処理 */

  r = pthread_join( sub_thread_obj, (void**) &ret );  if ( r != 0 )  error();

  return  0;
}


int  sub_thread( void* p )
{
  /* サブスレッドの処理 */

  return  0;
}
pthread_t;
スレッドID を格納する変数の型です。
で取得できます。
同じ ID かどうかは
で判定します。
int  pthread_create( pthread_t* thread,  pthread_attr_t* attr,  void* (*start_routine)(void *),
                     void* arg );
#include <pthread.h>
【引数】
thread
attr
通常 NULL
start_routine
スレッドが実行する関数(→下記)
arg
start_routine の第1引数に渡す値
返り値
0=正常、他=エラーコード (error.h)
start_routine 引数の詳細
void*  routine( void* arg );
arg
【引数】
pthread_create の第4引数(arg)
返り値
リターン値、pthread_join の第2引数(ret)に渡す値
スレッドを生成します。
(スレッド関数)
型変数のアドレス(未初期化のもの)
int  pthread_join( pthread_t thread,  void** ret );
#include <pthread.h>
【引数】
thread
終了を待つスレッド、pthread_create したもの
ret
(出力)スレッド関数の返り値(下記)、取得しない=NULL
返り値
0=正常、他=エラーコード (error.h)
他のスレッドの終了を待ちます。 同時にスレッドの返り値を取得します。
ret 引数の詳細
の第3引数(start_routine) で指定したスレッド関数の返り値を格納する
アドレスを指定します。
返り値の他に、
pthread_exit
の第1引数(ret) に指定した値も格納できます。
pthread_t  pthread_self( void );
現在実行中のスレッドID を返します。
int  pthread_equal( pthread_t t1, pthread_t t2 );
スレッドID が同じかどうか判定します。 同じなら非0 です。
void  pthread_exit( void* ret );
自分自身のスレッドを終了します。 スレッド関数の中から実行します。
【引数】
ret
スレッド関数のリターン値、pthread_join の第2引数(ret)に渡す値
【補足】
の第3引数(start_routine) で指定したスレッド関数から呼び出した関数
から、すぐにスレッドを終了するときに使います。 start_routine の中で、return すること
と同じです。
他のスレッドにスレッドの終了を通知しないスレッドです。
pthread_t  t;
pthread_attr_t  attr;

pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
pthread_create( &t, &attr, func, NULL );
デタッチスレッドを生成するコードです。
途中で、デタッチスレッドに変えるときは、pthraed_detach を使います。
int  pthread_detach( pthread_t* t );
キーワード:
pthraed_detach, pthread_attr_t, pthread_attr_init,
pthread_attr_setdetachstate, PTHREAD_CREATE_DETACHED
一連の処理の途中で、他のプロセス(スレッド)がアクセスして、正しくない状態ができることがない
ようにします。 排他制御ができているものを、スレッドセーフなものと言うことができます。
考え方
Win32API
HANDLE  sema_for_n = CreateSemaphore( NULL, 1, 1, "sema_for_n" );
WaitForSingleObject( sema_for_n, INFINITE );  // 排他開始

n++;

ReleaseSemaphore( sema_for_n, 1, NULL );  // 排他終了
CloseHandle( sema_for_n );
たとえば、共有変数 n に対して、複数のスレッドで n++ する場合、排他制御が必要です。
なぜなら、n++ は、アセンブラでは次のようになり、n にライトする値が、n をリードした値に
依存しているためです。
スレッドA
スレッドB
ld   r1, n       // n の値をメモリからリード
add  r1, r1, 1   // n + 1 を計算
st   r1, n       // n の値をメモリへライト
ld
ld
add
st
add
st
n
1
2
2
1
(3になるべき)
これを2つのスレッドが実行すると、下記のように、+2 されないことがあります。
すべてのスレッドで、下記のように排他制御してください。
排他制御は、
排他制御を行う区間を、
1つの変数を複数のスレッドでアクセスすることを、データレースと呼びます。
相互排除(mutual exclusion)
と呼ぶこともあります。
と呼びます。
クリティカルセクション
HANDLE  mutex_for_n;
BOOL    b;

mutex_for_n = CreateMutex( NULL, FALSE, "mutex_for_n" );
if ( mutex_for_n == NULL )  error();
WaitForSingleObject( mutex_for_n, INFINITE );  // 排他開始

n++;

b = ReleaseMutex( mutex_for_n );  // 排他終了
if ( ! b )  error();
CloseHandle( mutex_for_n );
同じスレッドなら、クリティカルセクションをネストして入ることができます。
同じスレッドでも、クリティカルセクションにネストして入ることができません。
#include  <pthread.h>

pthread_mutex_t  mutex;
int  r;

r = pthread_mutex_init( &mutex, NULL );  if ( r != 0 )  error();
r = pthread_mutex_lock( &mutex );  if ( r != 0 )  error();  // 排他開始

n++;

r = pthread_mutex_unlock( &mutex );  if ( r != 0 )  error();  // 排他終了
r = pthread_mutex_destroy( &mutex );  if ( r != 0 )  error();
pthread_mutex_lock で待ちに入りたくないときは、pthread_mutex_trylock を使います。
pthread_mutex_t 型の変数は、複数のスレッドで共有するので、
該当するスレッドに渡す必要があります。
B
A
setA( 3 )
  A = 3
  r = A
  B = r + 1
setB( 5 )
  B = 5
  r = B
  A = r - 1
のように、不変式があるとき(2変数の間で関係を常に満たす必要があるとき)は、
1
2
A = 3
r = 3
B = 5
r = 5
A = 4
B = 4
3
5
4
4
NG
ライトする変数と、それに依存する変数をロックしてから、リードライトします。
ロックする順番 (A→B) は
常に同じに順番にすること

複数ロックするときは、
グローバルロックします。
setB( 5 )
  Lock( A )
  Lock( B )
  B = 5
  r = B
  A = r - 1
  Unlock( A )
  Unlock( B )
setA( 3 )
  Lock( A )
  Lock( B )
  A = 3
  r = A
  B = r + 1
  Unlock( A )
  Unlock( B )
OK
4
4
5
3
B = 5
r = 5
A = 4
A = 3
r = 3
B = 4
2
1
A
B
AとBの両方をリードするときも、ロックしないと、必要な関係を満たしたものが得られません。
(片方だけなら問題ありません)
getAB( a, b )
  a = A
  b = B
setA( 3 )
  Lock( A )
  Lock( B )
  A = 3
  r = A
  B = r + 1
  Unlock( A )
  Unlock( B )
3
4
B
A
1
2
A = 3
r = 3
B = 4
2
3
a = 3
b = 2
A + 1 = B
A + 1 = B の
関係ではない
getAB( a, b )
  Lock( A )
  Lock( B )
  a = A
  b = B
  Unlock( A )
  Unlock( B )
a = 3
b = 4
(待ち)
(待ち)
A + 1 = B の関係が
満たされている
A または B の変数にアクセス(リード、ライト)するときに排他制御をしないと、次のように問題が発生します。
なお、定数に依存している場合は、ロックする必要はありません。
  Lock( G )
  Lock( A )
  Lock( B )
  Unlock( G )
不変式があるときは、ロックした直後と、アンロックする直前で、不変式のチェックをするとよいでしょう。
状態を表す変数は、状態が変化している最中にアクセスされないようにします。
なぜなら、状態は必ず、他の何かに依存関係があるだけでなく、状態変数自身に
依存関係があるからです。
if ( state == 1 ) {
  seek( 1 );
  read( 1 );
  state = 2;
}
if ( state == 1 ) {
  seek( 1 );
  read( 1 );
  state = 2;
}
if ( state == 1 ) {
  seek( 1 );
  read( 1 );  // 1
  state = 2;
}
if ( state == 1 ) {
  seek( 1 );




  read( 1 ); // 2
  state = 2;
}
WaitForSingleObject( mutex );
if ( state == 1 ) {
  chgToState2();
  state = 2;
}
ReleaseMutex( mutex );
排他制御しないと、次のようにエラーになります。
初期化(state=0から1へ)に関しては、特別に pthread_once があります。
また、初期化に関して排他制御されているものをパッケージセーフと呼びます。
1つ1つの変数をロックしていると、排他制御が複雑になるだけでなく、処理効率が悪くなります。
そこで、依存関係があるメンバ変数を持った1つの構造体(クラス)に対して全体をロックすると
いいでしょう。
Obj_setA( this, 3 )
  Lock( this )
  this->A = 3
  this->B = 3 + 1
  Unlock( this )
依存関係のないメンバ変数にアクセスするときは、ロックする必要はありません。
Obj_setA( t, 3 )
  Lock( t )
  t->A = 3
  t->B = 3 + 1
  Unlock( t )
Obj_setCD( t, 5, "abc" )
  t->C = 5
  t->D = "abc"
クラスのメンバ関数できちんとロックが行われていれば、外部でロックを意識する必要はありません。
Lock( t );    // 不要
Obj_setA( t, 3 )
Unlock( t );  // 不要
Obj_setACD( t, 3, 5, "abc" )
  Lock( t )
  t->A = 3
  t->B = 3 + 1
  Unlock( t )
  t->C = 5
  t->D = "abc"
Obj_setA( t, 3 )  // OK
ただし、メンバ関数のロックが完全でも、外部でロックを意識する必要があるケースがあります。
すべてのメンバ関数で、コンパイラが自動的にロックするコードを出力するものを、モニタロックと
呼びます。
構造化データロッキングモニタの一般形式
がきちんと行われていれば、基本的に外部はロックを気にする必要がありませんが、
クラスに依存関係ができた場合は、ロックをしなければなりません。
Obj_setAC( t, 3, 5 )
  Lock( t )
  t->A = 3
  t->B = 3 + 1
  Unlock( t )
  t->C = 5
Site_setAC( 3, 5 )
  Lock( t )
  Obj_setAC( t, 3, 5 )
  AA = 3
  CC = 5
  UnLock( t )
ロックをしなかったら、次のようになります。
Site_setAC( 3, 5 )
  Obj_setAC( t, 3, 5 )
  AA = 3
  CC = 5
Site_setAC( 7, 9 )
  Obj_setAC( t, 7, 9 )
  AA = 7
  CC = 9
t->A = 7
t->B = 8
t->C = 9
AA = 7
CC = 9
t->A = 3
t->B = 4
t->C = 5
AA = 3
CC = 5
t->A == AA && t->C == CC
の関係を満たす必要がある場合、
t->A = 7,  AA = 3
t->C = 9,  CC = 5
関係を満たさなくなってしまった
ライト(更新)に時間がかかる場合、古いデータのリードならロックせずともできます。
ただし、リードするデータに依存する他のデータもリードしない場合に限ります。
依存する場合は、リードロックだけ行います。
新しいデータを得るときは、ライトロックを行います。
この場合、A と newA には、依存関係がありますが、常に A = newA である必要はありません。
ただし、多くの場合、次の機能が必要になります。
A != newA (矛盾状態)かどうかをすぐに確認する機能
A = newA (正規状態)になったら通知することを予約する機能
(待ち)
Lock( Aw )
a = A
Unlock( Aw )
getNowA( a )
  Lock( Aw )
  a = A
  Unlock( Aw )
a = A
B = A
Unlock( Br )
Unlock( Ar )
Unlock( Bw )
Unlock( Aw )
Lock( A )
Lock( B )
newA = A
newA += 3
Lock( Ar )
Lock( Br )
A = newA
getOldA( a )
  a = A
setA( 3 )
  Lock( Aw )
  Lock( Bw )
  newA = A
  newA += 3
  Lock( Ar )
  Lock( Br )
  A = newA
  B = A
  Unlock( Br )
  Unlock( Ar )
  Unlock( Bw )
  Unlock( Aw )
getOldAB( a, b )
  Lock( Ar )
  Lock( Br )
  a = A
  b = B
  Unock( Ar )
  Unock( Br )
Lock( Ar )
Lock( Br )
a = A
b = B
Unlock( Br )
Unlock( Ar )
(待ち)
A == B
の条件があるとき、A のコピーである a をバッファに入れれば、
B = a
を実行する処理を
後回しにして、A だけは次の処理をすることができます。
ただし、B は、
しかできません。
setA( 3 )
  Lock( G )
  Lock( Aw )
  postB( 3 )





  Unlock( G )
  A = 3
  Unlock( Aw )
Lock( a )
LockI( Bw )
a[0] = 3
i=1
Rel( b )
Unlock( a )
setA( 5 )
  Lock( G )
  Lock( Aw )
  postB( 5 )




  Unlock( G )
  A = 5
  Unlock( Aw )
threadB( )
for (;;) {
  Lock( a )
  if (i=0) {
    UnlockI( Bw )
    Unlock( a )
    Wait( b & a )
    Reset( b )
  }
  r = a[i--]
  Unlock( a )
  newB = B
  Lock( Br )
  B = r
  Unlock( Br )
}
Lock( a )
a[1] = 5
i=2
Rel( b )
Unlock( a )
Lock( Br )
B = 3
Unlock( Br )
Lock( a )
5 = a[1]
Unlock( a )
Lock( Br )
B = 5
Unlock( Br )
Lock( a )
UnlockI( Bw )
Wait( b & a )
setA( 3 )
  Lock( G )
  Lock( Aw )
  postB( 3 )
  Unlock( G )
  A = 3
  Unlock( Aw )
postB( 3 )
  Lock( a )
  if (i=0)
    LockI( Bw )
  a[i] = 3
  i ++
  Rel( b )
  Unlock( a )
getNowB( out )
  Lock( G )
  Lock( Bw )
  Unlock( G )
  out = B
  Unlock( Bw )
  Lock( Bw )
  Unlock( G )
  out = B
  Unlock( Bw )
待ち( G )
getOldB( out )
  Lock( Br )
  out = B
  Unlock( Br )
Lock( Br )
out = B
Unlock( Br )
LockI は、ロックしたスレッドと別のスレッドで、アンロックができるタイプです。
Wait( b & a )
   のリターン
Reset( b )
3 = a[0]
Unlock( a )
書きかけ
postB も行う WaitForMultipleObjects
デストラクタの活用
依存関係が平行しているときは、依存するデータのコピーを作成することにより、ロックする範囲を
分割できます。
A == B == C
の条件があるとき、
B = A
にする処理と、
C = A
にする処理に分けられます。
この2つの処理は、マルチスレッドで処理させることができます。
setA( 3 )
  Lock( A )
  Lock( B )
  Lock( C )
  A = 3
  postB( 3 )
  postC( 3 )
  Unlock( A )
  (Wait(B,C))
postB( 3 )
  B = 3
  Unlock( B )
postC( 3 )
  C = 3
  Unlock( C )
左の Wait は、両方のスレッドが終わるのを
待ちますが、不要なこともあります。
メッセージ渡し
メッセージ渡し
マルチスレッドなので、一時的に片方だけ条件を満たす(A==B && A!=B または A!=B && A==B) の
時間があります。 このとき、B または C のロックが外れているので、B または C のどちらかにアクセス
することが可能です。
setA( 3 )
postB( 3 )
postC( 3 )
Lock( A )
Lock( B )
Lock( C )
A = 3
postB( 3 )
postC( 3 )
Unlock( A )
B = 3
Unlock( B )
(待ち)
C = 3
Unlock( C )
優先処理
非優先処理
この状態で、次の A, B の処理ができます。
setA( 5 )
B = 5
Unlock( B )
postC( 5 )
Unlock( A )
Lock( A )
Lock( B )
Lock( C )
A = 3
postB( 5 )
C = 5
Unlock( C )
A, B はアイドル状態
ロックの例外
タイムアウト
中断メッセージ(待機要求)
書きかけ
if ( GetMutex ) {
  StartLockSection
  t = GetTime()
  lock(A->lockee)
  lock(B->lockee)
  lock(C->lockee)
  EndLockSection
}
unlock( B->lockee )
unlockall()
lock
if timeout check deadlock
ロックされているか、未更新状態か
サービスルーチンが待ちに入ると、クライアントから要求を受け付けることができる状態になりますが、
処理を行っている状態(ビジー)では、クライアントからの要求を待つかバッファリングする必要が
あります。
n=0;       // NG
for (;;) { // NG
  wait();  // OK
  switch ( msg ) {         // NG
    case 1: n++;    break; // NG
    case 2: ret(n); break; // NG
  } // NG
}   // NG
printf( n );
n のリード要求
上記の場合、 n=0 が実行される前は、n の値は不定になってしまいます。
n++ が実行される前は、n の値は期待した値より 1 小さくなってしまいます。
レディ状態以外を排他するには、次のように記述します。 (バッファリングはしません)
受ける側(サーバ)
要求する側(クライアント)
HANDLE sema;

sema = CreateSemaphore( NULL, 0, 1, "MySema" );
for (;;) {
  ReleaseSemaphore( sema, 1, NULL );

  // 要求待ち(レディ状態)

  WaitForSingleObject( sema, INFINITE );

  // 応答処理
}
CloseHandle( sema );
HANDLE  sema;

sema = CreateSemaphore( NULL, 0, 1, "MySema" );
WaitForSingleObject( sema, INFINITE );

// サーバへ要求

ReleaseSemaphore( sema, 1, NULL );
CloseHandle( sema );
複数のスレッド(トランザクション)が、他がロックしたリソースをロックしようとして、
処理が進まなくなること。
食事をする哲学者(Dining Philosophers)の問題。
LockA
LockB
スレッドA
LockA
LockB
スレッドB
リソースA
リソースB
×
×
データベース・システムは、これを自動的に検出し、ロールバックやリトライをする。

ロックする順番をシステム全体で統一すれば、発生しない。
待ち続ける
待ち続ける
テスト方法:
マルチスレッドで耐久テストする
データの自動チェックをつけて
ビジー状態での対応
ビジー状態に対するユーザの操作は次のものが考えられます。
ビジー状態だったら、リクエストをキャンセルさせる。
待機後実行
割り込み実行
優先的なリクエストを先に処理させる
次にメッセージを受け付けるように、バッファにためる。 待ち状態を表示。
ビジー状態かどうかの変数をリードして、ビジーならすぐ戻る。
リードするときに、排他制御は不要。
マルチスレッドでは、同期している以外の部分は、他のスレッドがいくらでも動く可能性があります。
1行(1アセンブラ命令)次の命令でも、他のスレッドに割り込まれることがあると思ってください。
// 通知前は、相手の wait より後は実行されない(*2)
SetEvent( ev );
// 相手の ev 待ちより後は実行される
WaitForSingleObject( sema );
  // こちらが後になることもある(*1)
WaitForSingleObject( ev );
ResetEvent( ev );
Sleep( 1000 );
WaitForSingleObject( sema );
  // こちらが先になることもある
上記のように、SetEvent の直後に sema を要求しても、sema を取得するのが後になることが
あります。
SetEvent の前までは、SetEvent の対象となる待ちより後は実行されません。
// 相手の ev 待ちより後は実行されない
for (;;) {
  // 相手の ev 待ちより後は実行される
  SetEvent( ev );
}
ただし、ループしていたら実行される可能性があります。
(*1)

(*2)
すぐ下の命令を続けて実行する可能性
volatile 変数の注意点
volatile 変数にアクセスすると、必ずメモリアクセスが発生し、アクセス順序が保障されます。
一部のコンパイラやプロセッサは、アウトオブオーダー(アセンブラの順序に従わない実行、または
アクセス)のものがあります。 そういったプロセッサでは、オーダーを制御する特殊なアセンブラが
用意されています。
自動保存や印刷のマルチスレッド
オブジェクトのコピーを作成してから、保存を行います。
保存に必要なデータは、すべてコピーをとります。
高速にコピーができれば、マルチスレッドにしないで(排他制御しないで)コピーができます。
そのために、ある程度メモリ領域を確保してから、コピーを始めるとよいでしょう。
コピーは、保存のためだけに使うので、排他制御は必要ありません。
データ
コピーデータ
編集の続き
ファイルに保存
後始末処理のマルチスレッド
後始末処理が呼ばれたということは、今後、そのオブジェクトは使われないことが保障されている。
そこで、リスト構造のトップのみ切り離して、優先度の低いスレッドで後始末を行えばよい。
ptr = list
ptr = NULL;
delete  list;
優先度の低いスレッド
通常の優先度のスレッド
ミューテックスは、スレッド間やプロセス間で排他制御(ロック/アンロック)を行うために使います。
取得したスレッドしか開放できないので、スレッド間通信に使うことはできません。
#include <windows.h>
#define  error() {\
  LPTSTR  msg;\
  DWORD   code = GetLastError();\
\
  FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |\
      FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,\
      NULL, code, LANG_USER_DEFAULT, (LPTSTR)&msg, 0, NULL );\
  printf( "%s\n", msg ); \
  LocalFree( msg );\
}

#include <stdio.h>


int  main( int argc, char* argv[] )
{
  HANDLE  mutex_for_n;
  BOOL    b;

  mutex_for_n = CreateMutex( NULL, FALSE, "mutex_for_n" );
  if ( mutex_for_n == NULL )  error();

printf( "1-1\n" );
  WaitForSingleObject( mutex_for_n, INFINITE );  // 排他開始
printf( "1-2\n" );
  WaitForSingleObject( mutex_for_n, INFINITE );  // 排他開始
printf( "1-3\n" );

  Sleep( 10000 );

printf( "1-7\n" );
  b = ReleaseMutex( mutex_for_n );  // 排他終了
  if ( ! b )  error();
printf( "1-8\n" );
  b = ReleaseMutex( mutex_for_n );  // 排他終了
  if ( ! b )  error();
printf( "1-9\n" );

  CloseHandle( mutex_for_n );

  return 0;
}
#include <windows.h>
#define  error() { \
  LPTSTR  msg; \
  char  s[256]; \
  DWORD   code = GetLastError(); \
 \
   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | \
      FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, \
      NULL, code, LANG_USER_DEFAULT, (LPTSTR)&msg, 0, NULL ); \
   sprintf( s, "ERR(%d): %s\n", __LINE__, msg );  \
   MessageBox( NULL, s, "", MB_OK ); \
   LocalFree( msg ); \
}

#include <stdio.h>


int  main( int argc, char* argv[] )
{
  HANDLE  mutex_for_n;
  BOOL    b;

  mutex_for_n = CreateMutex( NULL, FALSE, "mutex_for_n" );
  if ( mutex_for_n == NULL )  error();

printf( "2-1\n" );
  WaitForSingleObject( mutex_for_n, INFINITE );  // 排他開始
printf( "2-2\n" );

  Sleep( 10000 );

printf( "2-8\n" );
  b = ReleaseMutex( mutex_for_n );  // 排他終了
  if ( ! b )  error();
printf( "2-9\n" );

  CloseHandle( mutex_for_n );

  return 0;
}
1.cpp
2.cpp
1.cpp の Sleep から抜けるまで、2.cpp のミューテックスの中に入ることができないサンプルです。
サンプルでは、プロセス間で排他制御を行うため、名前つき Mutex になっていますが、
実際は同一プロセス内のスレッド間で排他制御を行う名前なし Mutex を使うことが多いです。
1.cpp
2.cpp
WaitForSingleObject
WaitForSingleObject
ReleaseMutex
ReleaseMutex
Sleep( 10000 )
WaitForSingleObject
ReleaseMutex
Sleep( 10000 )
対応: Win32
#include  <stdio.h>
#include  <stdarg.h>
#include  <stdlib.h>
#include  <unistd.h>
#include  <pthread.h>

typedef  void* (*pthread_f)(void *);
int  sub_thread( void* p );


#define  error()  my_error_imp( __FILE__, __LINE__, -1, "" )
int   my_error_imp( char* file, int line, int id, const char* fmt, ... )
  { if ( fmt[0]!='\0' )  { va_list va;  va_start( va, fmt );  vprintf( fmt, va );  va_end( va ); }
    printf( "ERROR %d in (%d) %s\n", id, line, file );
    exit(4);  return id; }



int  main()
{
  pthread_t  sub_thread_obj;
  int  r;
  int  ret;
  pthread_mutex_t  mutex;

  /* 初期化します、スレッドを生成します */
  r = pthread_mutex_init( &mutex, NULL );  if ( r != 0 )  error();
  r = pthread_create( &sub_thread_obj, NULL, (pthread_f)sub_thread, &mutex );


  /* 排他制御して printf します */
  printf( "main1\n" );

  r = pthread_mutex_lock( &mutex );  if ( r != 0 )  error();  // 排他開始

  printf( "main2\n" );
  usleep(1000);
  printf( "main3\n" );

  r = pthread_mutex_unlock( &mutex );  if ( r != 0 )  error();  // 排他終了

  usleep(1000);
  printf( "main4\n" );

  /* 後始末します */
  r = pthread_join( sub_thread_obj, (void**) &ret );
  r = pthread_mutex_destroy( &mutex );  if ( r != 0 )  error();

  return  0;
}



int  sub_thread( void* p )
{
  pthread_mutex_t*  mutex = (pthread_mutex_t*) p;
  int  r;

  /* 排他制御して printf します */
  printf( "sub1\n" );

  r = pthread_mutex_lock( mutex );  if ( r != 0 )  error();

  printf( "sub2\n" );
  usleep(1000);
  printf( "sub3\n" );

  r = pthread_mutex_unlock( mutex );  if ( r != 0 )  error();

  usleep(1000);
  printf( "sub4\n" );

  return  0;
}
2つのスレッドで、ミューテックスを使って排他制御します。
ロックしている間に、他のスレッドを動かす usleep を入れていますが、
ミューテックスによって別のスレッドは動きません。
ソース
出力結果
main1
sub1
main2
main3
sub2
main4
sub3
sub4
対応: POSIX
… main2 と main3 の間の usleep で、sub2 は表示されません。
HANDLE  CreateMutex( SECURITY_ATTRIBUTES* secur,
                     BOOL bInit,  TSTR* name );
【引数】
secur
セキュリティ関係(使わないなら NULL)
bInit
ミューテックスを作成します。
name
ミューテックスの名前、プロセス間で排他制御しない=NULL
返り値
ミューテックスのハンドル
すぐにミューテックスを取得するかどうか
対応: Win32
BOOL  ReleaseMutex( HANDLE mutex );
ミューテックスによるクリティカルセクションの終了をします。
対応: Win32
int  pthread_mutex_init( pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr );
ミューテックスを初期化します。
【引数】
mutex
mutexattr
ミューテックス
通常 NULL
対応: POSIX
返り値
成功=0
サンプル
int  pthread_mutex_lock( pthread_mutex_t* mutex );
排他制御を開始します。(ロックします) ロックできるまで関数の中で待ちます。
【引数】
mutex
ミューテックス
対応: POSIX
返り値
成功=0
サンプル
int  pthread_mutex_trylock( pthread_mutex_t* mutex );
排他制御を開始します。(ロックします) すでにロック状態ならすぐエラーを返します。
【引数】
mutex
ミューテックス
対応: POSIX
返り値
成功=0
サンプル
int  pthread_mutex_unlock( pthread_mutex_t* mutex );
排他制御を終了します。(ロック解除します)
【引数】
mutex
ミューテックス
対応: POSIX
返り値
成功=0
サンプル
int  pthread_mutex_destroy( pthread_mutex_t* mutex );
ミューテックスを削除します。
【引数】
mutex
ミューテックス
対応: POSIX
返り値
成功=0
サンプル
初期化します
初期化します(マルチコアでのスピン付き)
後始末します
クリティカルセクションを開始します(待ちあり)
クリティカルセクションを開始します(待ちなし)
クリティカルセクションを終了します
同じプロセスの中で、排他制御します。
SMPでは複数プロセッサの間で排他制御が可能です。
void  InitializeCriticalSection( CRITICAL_SECTION* c );
クリティカルセクションを初期化します。
関連
関連
void  InitializeCriticalSectionAndSpinCount( CRITICAL_SECTION* c, DWORD spin_count );
スピンカウント付きの、クリティカルセクションを初期化します。(マルチコア専用)
マルチコアの CPU で、別の PE がクリティカルセクションに入っていた場合、
内部で WaitForSingleObject を呼び出す前に、spin_count だけ再試行します。
クリティカルセクションが小さいときに、コンテキストスイッチが起きにくくなるので、
スレッドの遅延が少なくなります。
spin_count が小さいほど、他のスレッドが実行状態になりやすくなります。
Windows NT では、4000 を設定しています。

シングルコアでは、スピンしなくても、優先順位が高ければ、遅延は少なくなります。(推測)
void  DeleteCriticalSection( CRITICAL_SECTION* c );
クリティカルセクションを後始末します。
void  EnterCriticalSection( CRITICAL_SECTION* c );
クリティカルセクションを開始します。
入れなければ待ちます。
関連
BOOL  TryEnterCriticalSection( CRITICAL_SECTION* c );
クリティカルセクションを開始します。
入れなければ FALSE を返します。
関連
void  LeaveCriticalSection( CRITICAL_SECTION* c );
クリティカルセクションを終了します。
単純に1つの別のスレッドの処理が終わるまで待つ方法でも、いくつかあります。
スレッドが終了するのを待つ場合:
スレッドが持つメッセージループの中で処理が終わるのを待つ場合:
waitpid
int ThreadFunc()
{
  return  0;
}
WaitForSingleObject( thread ); // 完了待ち

// 完了後の処理
int ThreadFunc()
{
  for (;;) {
    SetEvent();
    WaitForSingleObject();

    // 応答処理
  }
}
上記コードは正しくないかもしれません
WaitForSingleObject( event ); // 完了待ち

// 完了後の処理
pthread_cond_t  cond;
pthread_cond_init( &cond, NULL );
pthread_cond_wait( &cond, &rel_mutex );


pthread_cond_destroy( &cond );
クリティカルセクションの中で pthread_cond_wait する場合、
rel_mutex を指定して、待っている間だけミューテックスを開放します。

開放するので、不変式があれば、wait に入る前に、不変式を満たしておく必要があります。

pthread_cond_wait から抜けるのは、cond がシグナル状態 AND ミューテックスが unlock された
ときです。
pthread_cond_signal( &cond );
get( ) {
  pthread_mutex_lock( &mutex );
  while ( empty )  pthread_cond_wait( &not_empty_cond, &mutex );

  get;  full = false;

  pthread_mutex_unlock( &mutex );
  pthread_cond_signal( &not_full_cond );
}
add( ) {
  pthread_mutex_lock( &mutex );
  while ( full )  pthread_cond_wait( &not_full_cond, &mutex );

  add;  empty = false;

  pthread_mutex_unlock( &mutex );
  pthread_cond_signal( &not_empty_cond );
}
関数内部で待つ処理がある場合、待ち条件を解除する関数からシグナルを送ること。
シグナル状態になるまで待ちます。
pthread_cond_wait 関数は、それまでに signal 状態だったものを、非 signal 状態にします。
このため、while 条件文と、wait 関数の間で割り込まれたときに、他のスレッドが、
while 条件を満たさないようにしたときに、デッドロックしてしまいます。
mutex は、これを避けるためにあります。
(そもそも、full と cond に依存関係があることが、複雑さを招いているので、
 セマフォを使うとよいでしょう。)
while ( full ) {


  pthread_cond_wait();  // 非シグナル状態にして待つ
                        //  →デッドロック
}
full = false;
pthread_cond_signal();
pthread_cond_wait の代わりに pthread_cond_timewait でもよい。
AND 条件の待ち方
while ( ! cond1 || ! cond2 ) {
  if ( ! cond1 )  pthread_cond_wait( &not_cond1, &mutex );
  if ( ! cond2 )  pthread_cond_wait( &not_cond2, &mutex );
}
キーワード
pthread_cond_t, pthread_cond_init, pthread_cond_wait, pthread_cond_destroy
共通API
BOOL  CloseHandle( HANDLE handle );
セマフォやイベントなどのハンドルの使用を終了します。

ハンドルの対象となるものの実体は、すべてのスレッドで使っているハンドルがすべて
Close されたときに、初めて削除されます。
handle に指定できるもの
→ セマフォ
DWORD  WaitForSingleObject( HANDLE handle, DWORD timeout_msec );
資源を確保したり通知を受けたりするなど、待ちのある処理を実行します。
【引数】
handle
処理を行う対象、待つ対象(下記)
timeout_msec
タイムアウト(ミリ秒)、タイムアウトしない(無限に待つ)= INFINITE
返り値
WAIT_OBJECT_0、WAIT_TIMEOUT など (下記)
handle に指定できるもの
セマフォ
イベント
その他
カウント値が 0 のときは、プラスされるまで待ちます
イベントが ResetEvent 状態なら待ちます
→ セマフォ
所有権を持ってたスレッドが所有権を解放しないで終了しました。
返り値の意味
WAIT_OBJECT_0
handle が、正常に、シグナル状態になりました。
WAIT_TIMEOUT
シグナル状態にならないで timeout_msec が経過しました。
WAIT_FAILED
handle が無効であるなど、その他のエラーです。
DWORD  WaitForMultipleObjects( DWORD n, HANDLE* handles, BOOL bAll, DWORD timeout_msec );
複数(AND/OR)の待つ対称を指定できる
【引数】
handles
処理を行う対象、待つ対象の配列
timeout_msec
タイムアウト(ミリ秒)、タイムアウトしない(無限に待つ)= INFINITE
返り値
(下記)
n
待つ対象の数
bAll
すべて待ちでない状態になったら抜ける
WAIT_OBJECT_0
WAIT_ABANDONED_0
です。
返り値の意味
bAll = TRUE のとき
すべてシグナル状態になりました
すべてシグナル状態になりましたが、
でした。
bAll = FALSE のとき
WAIT_OBJECT_0 〜 WAIT_OBJECT_0 + n - 1
シグナル状態になったもの
WAIT_ABANDONED_0 〜 WAIT_ABANDONED_0 + n - 1
でシグナル状態になったもの
bAll = TRUE / FALSE 共通
WAIT_TIMEOUT
シグナル状態にならないで timeout_msec が経過しました。
WAIT_FAILED
handle が無効であるなど、その他のエラーです。
その中で、1つ以上が
HANDLE  CreateFile( TSTR* path, DWORD access, DWORD share, SECURITY_ATTRIBUTES* secur,
                    DWORD creation,  DWORD attr, HANDLE template );
ファイルや、プロセス間通信を開きます。
【引数】
path
access
share
secur
creation
attr
template
ファイルパス、またはプロセス間通信名
access 引数は、ハンドルが行うものを指定します。
share 引数は、別のハンドルが同時にアクセスできるものです。
セキュリティ関係(使わないなら NULL)
CREATE_NEW
CREATE_ALWAYS
OPEN_EXISTING
OPEN_ALWAYS
TRUNCATE_EXISTING
新しく作るとき(ファイルが存在していないとき)のみ成功
既に存在しているときのみ成功
Write用:
Read用:
ファイルが存在していても成功(上書き)
ファイルがなければ、作成して成功
ファイルが存在しているときのみ成功(上書き)
GENERIC_READ、GENERIC_WRITE、その両方、アクセスフラグ(→MSDN)
NULL、FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_DELETE
成功条件、気にしないときは、CREATE_ALWAYS か OPEN_EXISTING
ファイルの種類、気にしないときは FILE_ATTRIBUTE_NORMAL
FILE_ATTRIBUTE_NORMAL
FILE_ATTRIBUTE_HIDDEN
通常はこれ
FILE_ATTRIBUTE_ARCHIVE
FILE_ATTRIBUTE_ENCRYPTED
暗号化属性 ON
アーカイブ属性 ON
隠し属性 ON
FILE_ATTRIBUTE_READONLY
読み取り専用属性 ON
FILE_ATTRIBUTE_SYSTEM
システムファイル属性 ON
上記を設定しないときは、下記の組み合わせ
FILE_ATTRIBUTE_TEMPORARY
テンポラリ(なるべくメモリへ)
その他あり → MSDN
NULL (内容は未調査)
返り値
ファイルなどのハンドル、失敗=INVALID_HANDLE_VALUE
creation 引数の詳細
attr 引数の詳細
→ サンプル
→ CreateFile に指定する値
参考
→ サンプル
現在使われていません。
CreateFile のサンプル
HANDLE  f;
BOOL    b;
char    buf[256];
DWORD   read_size;

f = CreateFile( TEXT("C:\\file.txt"), GENERIC_READ, FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( f == INVALID_HANDLE_VALUE )  error();

b = ReadFile( f, buf, sizeof(buf), &read_size, NULL );
if ( ! b )  error();

CloseHandle( f );
ライト時
リード時
HANDLE  f;
BOOL    b;
DWORD   wrote_size;

f = CreateFile( TEXT("C:\\file.txt"), GENERIC_WRITE, 0,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if ( f == INVALID_HANDLE_VALUE )  error();

b = WriteFile( f, "abc", 4, &wrote_size, NULL );
if ( ! b )  error();

CloseHandle( f );
BOOL  ReadFile( HANDLE handle, void* buf, DWORD size, DWORD* sized, OVERLAPPED* overlap );
ファイルやプロセス間通信で、データをリードします。
【引数】
handle
size
sized
overlap
返り値
ファイルハンドルや、プロセス間通信のハンドル、デバイスドライバのハンドル
リードしようとするサイズ(バイト)
(出力) リードしたサイズ(バイト)、NULL不可
buf
(出力) リードしたデータを格納する領域のアドレス
NULL か非同期処理関係、handle に FILE_FLAG_OVERLAPPED を指定したとき
パイプの書き込み側で書き込みが完了するか、指定されたバイト数の読み取りが終わるか、
エラーが発生した場合に戻ります。
成功=非0
タイムアウトは以下で指定できます。
メールスロット
(他未調査)
→ サンプル
BOOL  WriteFile( HANDLE handle, void* buf, DWORD size, DWORD* sized, OVERLAPPED* overlap );
ファイルやプロセス間通信で、データをライトします。
【引数】
handle
size
sized
overlap
返り値
ファイルハンドルや、プロセス間通信のハンドル、デバイスドライバのハンドル
ライトしようとするサイズ(バイト)
(出力) ライトしたサイズ(バイト)、NULL不可
buf
ライトしようとしているデータを格納した領域のアドレス
NULL か非同期処理関係、handle に FILE_FLAG_OVERLAPPED を指定したとき
成功=非0
→ サンプル
同じ機能を持った複数のプロセッサ(PE=Processor Element)が平行して動くシステム。
アムダールの法則:
SISD : 1つの命令で1つのデータを演算する命令(普通の命令)
SIMD : 1つの命令で複数のデータを演算する命令
MIMD : 1つの命令ワードに、複数の命令があるもの
共有変数の競合があった場合のみ処理をやり直す(ロールバックする)
1. Finding and creating concurrent tasks - 並列なタスクを見つけて作ること
2. Mapping tasks to threads - タスクをスレッドに割り当てること
3. Defining and implementing synchronization protocols - 同期に関する約束事を決めて実装すること
4. Dealing with race conditions - 競合状態を扱うこと
5. Dealing with deadlocks - デッドロックを扱うこと
6. Dealing with memory model - メモリモデルを扱うこと
7. Composing parallel tasks - 並列なタスクを構成すること
8. Achieving scalability - スケーラビリティを確保すること
9. Achiving portable & predictable performance - 性能が良く、予測可能にすること
10. Recovering from errors - エラーから回復すること
11. Dealing with all single thread issues - すべての単一のスレッド自身を扱うこと
Achive=達成する