マルチスレッドを実現する基本クラスによって、Linux, Windowsのプログラムを
かなり共通化できました。
しかし、同期処理を追加したことで、その部分の共通化が崩れてしまいました。
ここでは、同期処理のためのクラスを作成することで、再び共通化を図り、
これらを使用して、ネットワークプログラムをマルチスレッド化することにします。

次のようなクラスを作成します。
クラス名:CMySyncObject
ファイル名:MySyncObject.cpp、MySyncObject.h
Windows版ではクリティカルセクションを使用してみます。
PrintMACCriticalSectionを参考にしてPrintMACSyncを作成してます
Linux版ではミューテックスを使用してみます。
PrintMACMutexを参考にしてPrintMACSyncを作成します

【CMySyncObject】 パブリックメソッド BOOL Initialize(); // 初期化 BOOL Uninitialize(); // 開放 BOOL Lock(); // ロック(所有権取得待ち) BOOL UnLock(); // アンロック(所有権開放)

マルチスレッドプログラム 排他処理(同期制御)のクラス対応Windows版
前回作成したプロジェクト(PrintMACCriticalSection)をコピーし、フォルダ名、
ソリューション名、プロジェクト名をPrintMACSyncに変更します。
新規追加で、C++のクラスCMySyncObjectを追加します。
ファイル名:MySyncObject.cpp、MySyncObject.h

publicで公開されるメソッドは、WIndowsとLinux間で共通にします。
privateで、クリティカルセクションを定義しています。
もちろんミューテックス(ハンドル)を使って作成すいることもできます。

【MySyncObject.h】 #pragma once #include “stdThread.h” class CMySyncObject { public: CMySyncObject(); ~CMySyncObject(); BOOL Initialize(); // 初期化 BOOL Uninitialize(); // 開放 BOOL Lock(); // ロック(所有権取得待ち) BOOL UnLock(); // アンロック(所有権開放) private: CRITICAL_SECTION m_cs; // クリティカルセクションを使います };

MySyncObject.cppについては、PrintMACCriticalSectionで作成した内容と
同じなので、大丈夫でしょう。
Unlock()に念のためSleep(0)をコメントで記述しています。
他のスレッドが実行されないときはメントを外してください。

【MySyncObject.cpp】 #include “MySyncObject.h” CMySyncObject::CMySyncObject() { } CMySyncObject::~CMySyncObject() { } BOOL CMySyncObject::Initialize() { InitializeCriticalSection(&m_cs); // クリティカルセクションの初期化 return(TRUE); } BOOL CMySyncObject::Uninitialize() { DeleteCriticalSection(&m_cs); // クリティカルセクションの開放 return(TRUE); } BOOL CMySyncObject::Lock() // 所有権を取得するまで待つ { EnterCriticalSection(&m_cs); return(TRUE); } BOOL CMySyncObject::UnLock() // 所有権を開放する { LeaveCriticalSection(&m_cs); // Sleep(0); // 他のスレッドに実行のチャンスを与える return(TRUE); }

CMyThreadの同期処理の部分をCMySyncObjectに置き換えます。
置き換えた個所には★を書いています。

【MyThread.h】 #pragma once #include “ThreadJob.h” class CMySyncObject; // ★このクラスの使用することを宣言 // 必要なパラメータを構造体にする typedef struct { char cData; // 表示する文字 DWORD dwTimer; // 表示間隔(msec) DWORD dwCharacterInterval; // 文字間(msec) int iNum; // 表示する個数 CMySyncObject *pCMySyncObject; // ★同期オブジェクト } MyDataRec; class CMyThread : public CThreadJob { public: CMyThread(MyDataRec *pData); // パラメータをコンストラクタで渡す ~CMyThread(); // 基底クラスの関数をオーバーライドする // C++11で明示的にoverrideを書くことが出来るようになりました // 基底クラスの当該関数にvirtualが書いていないとエラーを出してくれます UINT DoWork() override; // cDataをdwTime(msec)間隔で表示する private: MyDataRec *m_pMyData; // コンストラクタで渡されるパラメータを格納 // このスレッド実行中領域が確保されていること };

【MyThread.cpp】 #include “MyThread.h” #include “MySyncObject.h” // ★CMySyncObjectを使うため //———————————————- // function // コンストラクタ // parameter // MyDataRec *pData [in]機能に必要な情報 // return // なし //———————————————- CMyThread::CMyThread(MyDataRec *pData) { m_pMyData = pData; } //———————————————- // function // デストラクタ // parameter // なし // return // なし //———————————————- CMyThread::~CMyThread() { } //———————————————- // function // 機能を記述した関数 // parameter // なし // return // 0(特にエラーもないので) //———————————————- UINT CMyThread::DoWork() { int ii; DWORD dwPrevTime = 0, dwNowTime; // 前回表示した時刻、現在時刻 while (!m_fStopFlag) { dwNowTime = timeGetTime(); // 現在時刻(Windowsの起動からのmsec) // 前回の表示時刻からdwTimer経過したか if (GetdwInterval(dwNowTime, dwPrevTime) <= m_pMyData-<dwTimer) { m_pMyData-<pCMySyncObject-<Lock(); // ★所有権を取得するまで待つ // 指定された条件で文字を表示する for (ii = 0; ii < m_pMyData-<iNum; ++ii) { fprintf(stderr, “%c”, m_pMyData-<cData); Sleep(m_pMyData-<dwCharacterInterval); } fprintf(stderr, “\n”); m_pMyData-<pCMySyncObject-<UnLock(); // ★所有権を開放する dwPrevTime = dwNowTime; // 表示した時刻を覚える } } return(1); }

メインプログラム(PrintMAC.cpp)同期オブジェクトをCMySyncObjectに
置き換えれば完成です。

【PrintMAC.cpp】 #include “MyThread.h” #include “MySyncObject.h” // ★CMySyncObjectを使うため int main(int argc, char *argv[]) { MyDataRec MyData[3] = { { ‘A’, 1000, 50, 10 }, {‘B’, 2000, 100, 5}, {‘C’, 3300, 200, 4} }; CMyThread *pCMyThread[3] = { NULL }; int ii; CMySyncObject *pCMySyncObject; // ★ pCMySyncObject = new CMySyncObject(); // ★ pCMySyncObject->Initialize(); // 同期オブジェクトの初期化 for (ii = 0; ii < 3; ++ii) // パラメータにセット MyData[ii].pCMySyncObject = pCMySyncObject; // for (ii = 0; ii < 3; ++ii) { pCMyThread[ii] = new CMyThread(&MyData[ii]); // パラメータ(構造体)のポインタを渡す pCMyThread[ii]->Begin(); // 開始 } getchar(); // Enterキー待ち for (ii = 0; ii < 3; ++ii) pCMyThread[ii]->End(); // スレッドに終了を通知 for (ii = 0; ii < 3; ++ii) { pCMyThread[ii]->WaitForEnd(); // 終了するのを待つ SAFE_DELETE(pCMyThread[ii]) // クラスのインスタンスの破棄 } pCMySyncObject->Uninitialize(); // ★同期オブジェクトの開放 SAFE_DELETE(pCMySyncObject) // return(0); }

メインプログラム(PrintMAC.cpp)には、Windows独自の記述がないことがわかります。
また、このプログラムの機能(文字を指定された条件で表示する)を記述している
MyThread.cppもWindows独自の記述はSleepだけだということがわかります。

次にマルチスレッドプログラム 排他処理(同期制御)のクラス対応Linux版
をやってみます。
前回作成したPrintMACMutexのLinux版を元にします。

前回作成したプロジェクト(PrintMACCriticalSection)をコピーし、フォルダ名、
ソリューション名、プロジェクト名をPrintMACSyncに変更します。
新規追加で、C++のクラスCMySyncObjectを追加します。
ファイル名:MySyncObject.cpp、MySyncObject.h

【makefile】 CC=g++ -g -O0 #CC=g++ PROGRAM=PrintMACSync OBJS=PrintMAC.o ThreadJob.o MyThread.o stdThread.o MySyncObject.o SRCS=$(OBJS:%.o=%.cpp) INCLUDE=stdThread.h LFLAGS=-lpthread $(PROGRAM):$(OBJS) $(SRCS) $(INCLUDE) $(CC) -o $(PROGRAM) $(SRCS) $(LFLAGS)

【MySyncObject.h】 #pragma once #include “stdThread.h” class CMySyncObject { public: CMySyncObject(); ~CMySyncObject(); BOOL Initialize(); // 初期化 BOOL Uninitialize(); // 開放 BOOL Lock(); // ロック(所有権取得待ち) BOOL UnLock(); // アンロック(所有権開放) private: pthread_mutex_t m_Mutex; // ミューテックスを使います };

【MySyncObject.cpp】 #include “MySyncObject.h” CMySyncObject::CMySyncObject() { } CMySyncObject::~CMySyncObject() { } BOOL CMySyncObject::Initialize() { pthread_mutex_init(&m_Mutex, NULL); // ミューテックスの初期化 return(TRUE); } BOOL CMySyncObject::Uninitialize() { pthread_mutex_destroy(&m_Mutex); // ミューテックスの破棄 return(TRUE); } BOOL CMySyncObject::Lock() // 所有権を取得するまで待つ { pthread_mutex_lock(&m_Mutex); return(TRUE); } BOOL CMySyncObject::UnLock() // 所有権を開放する { pthread_mutex_unlock(&m_Mutex); usleep(0); // 他のスレッドに実行のチャンスを与える return(TRUE); }

【MyThread.h】 #pragma once #include “ThreadJob.h” class CMySyncObject; // ★このクラスの使用することを宣言 // 必要なパラメータを構造体にする typedef struct { char cData; // 表示する文字 DWORD dwTimer; // 表示間隔(msec) DWORD dwCharacterInterval; // 文字間(msec) int iNum; // 表示する個数 CMySyncObject *pCMySyncObject; // ★同期オブジェクト } MyDataRec; class CMyThread : public CThreadJob { public: CMyThread(MyDataRec *pData); // パラメータをコンストラクタで渡す ~CMyThread(); // 基底クラスの関数をオーバーライドする // C++11で明示的にoverrideを書くことが出来るようになりました // 基底クラスの当該関数にvirtualが書いていないとエラーを出してくれます UINT DoWork() override; // cDataをdwTime(msec)間隔で表示する private: MyDataRec *m_pMyData; // コンストラクタで渡されるパラメータを格納 // このスレッド実行中領域が確保されていること };

【MyThread.cpp】 #include “MyThread.h” #include “MySyncObject.h” // ★CMySyncObjectを使うため //———————————————- // function // コンストラクタ // parameter // MyDataRec *pData [in]機能に必要な情報 // return // なし //———————————————- CMyThread::CMyThread(MyDataRec *pData) { m_pMyData = pData; } //———————————————- // function // デストラクタ // parameter // なし // return // なし //———————————————- CMyThread::~CMyThread() { } //———————————————- // function // 機能を記述した関数 // parameter // なし // return // 0(特にエラーもないので) //———————————————- UINT CMyThread::DoWork() { int ii; DWORD dwPrevTime = 0, dwNowTime; while (!m_fStopFlag) { dwNowTime = timeGetTime(); if (GetdwInterval(dwNowTime, dwPrevTime) >= m_pMyData->dwTimer) { m_pMyData->pCMySyncObject->Lock(); // ★所有権を取得するまで待つ for (ii = 0; ii < m_pMyData->iNum; ++ii) { fprintf(stderr, “%c”, m_pMyData->cData); usleep(m_pMyData->dwCharacterInterval * 1000); } fprintf(stderr, “\n”); m_pMyData->pCMySyncObject->UnLock(); // ★所有権を開放する dwPrevTime = dwNowTime; } } return(1); }

【PrintMAC.cpp】 #include “MyThread.h” #include “MySyncObject.h” // ★CMySyncObjectを使うため int main(int argc, char *argv[]) { MyDataRec MyData[3] = { { ‘A’, 1000, 50, 25 }, {‘B’, 2000, 100, 5}, {‘C’, 3300, 200, 4} }; CMyThread *pCMyThread[3] = { NULL }; int ii; CMySyncObject *pCMySyncObject; // ★ pCMySyncObject = new CMySyncObject(); // ★ pCMySyncObject->Initialize(); // ★同期オブジェクトの初期化 for (ii = 0; ii 3; ++ii) MyData[ii].pCMySyncObject = pCMySyncObject; for (ii = 0; ii 3; ++ii) { pCMyThread[ii] = new CMyThread(&MyData[ii]); // パラメータ(構造体)のポインタを渡す pCMyThread[ii]->Begin(); // 開始 } getchar(); // Enterキー待ち for (ii = 0; ii 3; ++ii) pCMyThread[ii]->End(); // スレッドに終了を通知 for (ii = 0; ii 3; ++ii) { pCMyThread[ii]->WaitForEnd(); // 終了するのを待つ SAFE_DELETE(pCMyThread[ii]) // クラスのインスタンスの破棄 } pCMySyncObject->Uninitialize(); // ★同期オブジェクトの開放 SAFE_DELETE(pCMySyncObject) return(0); }

マルチスレッド機能の実現、同期処理の実現部分についてかなり
単純化・共通化ができました
次回からネットワークプログラム(SimpleServer, SimpleClient)に
マルチスレッド機能を実現することにしましょう。

プロジェクトPrintMACSync for Windows

プロジェクトPrintMACSync for Linux