マルチスレッドでのファイル書き込み

[戻る]
wind 2002/09/11(水) 12:04:34
こんにちわ、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);
    }
 }
}

wood 2002/09/11(水) 12:27:43
>ファイル書き込み処理
の排他制御について聞きたいのではないのですか

つまり同時書き込みを防ぐには、どちらか一方で書き込み中はファイルロックする
必要があるのではないでしょうか

YuO [E-Mail] 2002/09/11(水) 12:29:12
同期オブジェクトはグローバルに持たせた方が使いやすいです。

こんな感じかなぁ……。
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でも同じです。
同期オブジェクトは,各スレッドごとに持つよりも,
グローバルに取ってしまった方が使いやすかったりします。

アイススケーター 2002/09/11(水) 12:42:17
クラスを利用しない場合

CreateMutex
OpenMutex
ReleaseMutex
CloseHandle

を利用すればいいですよ。

wind 2002/09/11(水) 16:00:45
みなさん、お返事ありがとうございます。m(__)m
いろいろ試してみましたが、どうもうまくいきません。
単体でやると上手くいくんですが、同時に実行するとやはり化けます。
排他制御以外に何か原因があるのでしょうか?
よろしくお願いします。

wood 2002/09/11(水) 16:07:52
遅延書き込み(OS)
コミット、ファイルフラッシュ(PG)
この辺どんな処理してますか

wind 2002/09/11(水) 16:34:42
ファイル書く関数は

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

という風になっています。
これ以外の処理は行っていません。
もし、何かあるようでしたご指摘願います。

wood 2002/09/11(水) 17:26:33
>CloseHandle( hFile );
の前に
BOOL FlushFileBuffers( HANDLE hFile); 
  
いらないのでしょうか

fwriteに対してはfflushしているようですけど?

wind 2002/09/11(水) 18:00:19
woodさんレスありがとうございます。
追加してみましてが駄目でした。

ログに書き出す際にsprintfを使って文字列を作成しているのですが、これは大丈夫なのでしょ
うか?
確かCreateThureadを使うときはstdio系の関数は使用不可だったような気がします。
サーバ側のDLLで複数クライアントから呼ばれる場合はどうなのでしょうか?

YuO [E-Mail] 2002/09/12(木) 07:14:17
> 確か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 2002/09/13(金) 11:19:33
はじめまして 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)) {}

# )が抜けていたので訂正しておきます。


以上、よろしくお願いします。

YuO [E-Mail] 2002/09/13(金) 11:40:29
> の部分で、"error C2238: ';' の前に不正なトークンがあります。"

私のミスですね……。
>    mutex_& oeprator= (const mutex_ &);
    mutex_& operator= (const mutex_ &);
             ^^

> その前に、上記では何をしているのか理解できないでいます。

コピーコンストラクタと代入演算子をprivateにしてさらに実装しないことで,複製できないようにしています。
複製できてしまうと,二つのインスタンスが同じハンドルを持ってしまい,問題が起きます。

コピーコンストラクタと代入演算子をprivateにして実装しないのは,オブジェクトの複製を禁止する一般的な方法です。

tetsu 2002/09/13(金) 11:51:50
tetsuです。

 YuOさんすばやい回答ありがとうございます。
 大変勉強になりました。
 mutexについていろいろ、勉強していきたいと思います。
 

古いスレッドには追加発言できません。