こんにちわ、windです。
マルチスレッド(DLLで)にて質問があります。
マルチスレッドで同じファイルに書き込みを行うのですが、順番待ちの仕方がわかりません。
下のようなソースを使用しているのですが、タイミングによっては同時に書き込まれてファイル
のフォーマットが乱れてしまします。
環境はWin2000、MSのVC++6.0です。
できればMFCを使用しない方法でご教授ください。
void LogWrite(char *strLog)
{
BOOL bFlag=TRUE;
BOOL bCreated=TRUE;
HANDLE hSyn = CreateSemaphore(NULL,1,1,SEMPHORE_NAME);
if ( !hSyn || hSyn != INVALID_HANDLE_VALUE )
{
bFlag = FALSE;
}
else
{
if (GetLastError() == ERROR_ALREADY_EXISTS )
bCreated =FALSE;
}
if (bFlag)
{
WaitForSingleObject(hSyn, INFINITE);
}
・
・
ファイル書き込み処理
ファイル書き込み処理
ファイル書き込み処理
ファイル書き込み処理
・
・
if (bFlag)
{
ReleaseSemaphore(hSyn,1,NULL);
if (!bCreated)
{
CloseHandle(hSyn);
}
}
}
>ファイル書き込み処理
の排他制御について聞きたいのではないのですか
つまり同時書き込みを防ぐには、どちらか一方で書き込み中はファイルロックする
必要があるのではないでしょうか
同期オブジェクトはグローバルに持たせた方が使いやすいです。
こんな感じかなぁ……。
namespace { // 外部からアクセスできないオブジェクト
class mutex_ { // Mutexの取得・解放を行うためのクラス
HANDLE hMutex;
// 複製は禁止
mutex_ (const mutex_ &);
mutex_& oeprator= (const mutex_ &);
public:
mutex_ (void);
~mutex_ (void);
bool wait (DWORD timeout = INFINITE) { return WaitForSingleObject(hMutex, timeout) == WAIT_OBJECT_0; }
bool release (void) { return ReleaseMutex(hMutex) != FALSE; }
} mutex;
mutex_::mutex_ (void) : hMutex(CreateMutex(NULL, FALSE, NULL) {}
mutex_::~mutex_ (void) { CloseHandle(hMutex); }
}
void LogWrite (const char * strLog)
{
mutex.wait();
// ここで書き込み処理
mutex.release();
}
クラスを使いましたが,
最初に書き込まれるより前にMutexが作成され,
使い終わった後にMutexを解放できるならクラスを使う必要はありません。
#namespaceスコープのクラスオブジェクトの構築・解体でそれを保証しているだけです。
今回はMutexを使いましたが,Semaphoreでも同じです。
同期オブジェクトは,各スレッドごとに持つよりも,
グローバルに取ってしまった方が使いやすかったりします。
クラスを利用しない場合
CreateMutex
OpenMutex
ReleaseMutex
CloseHandle
を利用すればいいですよ。
みなさん、お返事ありがとうございます。m(__)m
いろいろ試してみましたが、どうもうまくいきません。
単体でやると上手くいくんですが、同時に実行するとやはり化けます。
排他制御以外に何か原因があるのでしょうか?
よろしくお願いします。
遅延書き込み(OS)
コミット、ファイルフラッシュ(PG)
この辺どんな処理してますか
ファイル書く関数は
MutexでWait
・
・
if ((hFile = CreateFile( Path,
#ifndef WINNT
GENERIC_WRITE ,
#else
FILE_APPEND_DATA | SYNCHRONIZE,
#endif
FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
)) == INVALID_HANDLE_VALUE)
{
#ifndef WINNT
sprintf(MsgBuf + strlen(MsgBuf),GetLastError = %d \n, GetLastError());
OutputDebugString(MsgBuf);
#endif
fprintf(strerr,Error CreateFile %s 0x%0x\n,ERRFILENAME,GetLastError());
fwrite( MsgBuf, 1, strlen(MsgBuf), strerr );
fflush( strerr );
}
else
{
#ifndef WINNT
SetFilePointer(hFile, 0,NULL, FILE_END);
#endif
if( ! WriteFile( hFile,MsgBuf,strlen(MsgBuf),&intLen, NULL))
{
fprintf( stderr, Error WriteFile %s 0x%0x\n,Path,GetLastError());
fwrite( MsgBuf, 1, strlen(MsgBuf), strerr );
fflush( strerr );
}
CloseHandle( hFile );
}
・
・
ReleaseMutex
という風になっています。
これ以外の処理は行っていません。
もし、何かあるようでしたご指摘願います。
>CloseHandle( hFile );
の前に
BOOL FlushFileBuffers( HANDLE hFile);
いらないのでしょうか
fwriteに対してはfflushしているようですけど?
woodさんレスありがとうございます。
追加してみましてが駄目でした。
ログに書き出す際にsprintfを使って文字列を作成しているのですが、これは大丈夫なのでしょ
うか?
確かCreateThureadを使うときはstdio系の関数は使用不可だったような気がします。
サーバ側のDLLで複数クライアントから呼ばれる場合はどうなのでしょうか?
> 確かCreateThureadを使うときはstdio系の関数は使用不可だったような気がします。
> サーバ側のDLLで複数クライアントから呼ばれる場合はどうなのでしょうか?
DLLの場合,DllMainからはじめていて,
CRTとしてマルチスレッド用のものを使っていれば,問題なく使えます。
でもって,マルチスレッドということなので,
CreateMutex時に名前をNULLに指定しましたが,
複数のクライアントから呼ばれるのであれば,
何らかの名前を指定してすべてのDLLで同じMutexオブジェクトを共有する必要があります。
つまり,
> mutex_::mutex_ (void) : hMutex(CreateMutex(NULL, FALSE, NULL) {}
ここは
const char * const mutex_name = 2bd76e20-c5cb-11d6-968f-004026a7e5df; //さっき作ったUUID
mutex_::mutex_ (void) : hMutex(CreateMutex(NULL, FALSE, mutex_name) {}
のようにする必要があります。
はじめまして tetsuと申します。
便乗質問になります。
環境は WIN2K VC6++ SP5 です。
興味があったので実験しようと思ったのですが、
> mutex_& oeprator= (const mutex_ &);
の部分で、error C2238: ';' の前に不正なトークンがあります。
と出てしまいます。
> // 複製は禁止
> mutex_ (const mutex_ &);
> mutex_& oeprator= (const mutex_ &);
その前に、上記では何をしているのか理解できないでいます。
何をしているのか、ご教授お願いします。
> mutex_::mutex_ (void) : hMutex(CreateMutex(NULL, FALSE, NULL) {}
mutex_::mutex_ (void) : hMutex(CreateMutex(NULL, FALSE, NULL)) {}
# )が抜けていたので訂正しておきます。
以上、よろしくお願いします。
> の部分で、error C2238: ';' の前に不正なトークンがあります。
私のミスですね……。
> mutex_& oeprator= (const mutex_ &);
mutex_& operator= (const mutex_ &);
^^
> その前に、上記では何をしているのか理解できないでいます。
コピーコンストラクタと代入演算子をprivateにしてさらに実装しないことで,複製できないようにしています。
複製できてしまうと,二つのインスタンスが同じハンドルを持ってしまい,問題が起きます。
コピーコンストラクタと代入演算子をprivateにして実装しないのは,オブジェクトの複製を禁止する一般的な方法です。
tetsuです。
YuOさんすばやい回答ありがとうございます。
大変勉強になりました。
mutexについていろいろ、勉強していきたいと思います。