0

同時実行性が心配なコールバックを使用して非同期I/Oを実装しました。私は常に同じファイルで作業しており、OSファイルの物理I / Oは基本的に同期操作であるため、コールバックメソッドにロックメカニズムは必要ありませんが、ここでは間違っている可能性があります。 --SO:oと入力します)読み取り操作が完了したときに読み取りデータをバッファーキャッシュに入れるバッファーマネージャーと、EOverlappedStates列挙状態に基づく重複操作ごとの状態エンジンがあります。「I/Oが開始されていません」、「成功」、および「エラー」。私たちのようなマルチスレッドプログラムで同時実行性を確保するために、コールバックメソッドをロックする必要があると思いますか?

ファイルを開く:

OS_FILE_HANDLE CUniformDiskInterface::OpenFile(const wchar_t *fileName, bool *fileExists, bool readData, bool writeData, bool overlap, 
bool disableDiskCache, bool disableOsCache, bool randomAccess, bool sequentalScan) {
// Set access method
DWORD desiredAccess = readData ? GENERIC_READ : 0;
desiredAccess |= writeData ? GENERIC_WRITE : 0;

// Set file flags
DWORD fileFlags = disableDiskCache ? FILE_FLAG_WRITE_THROUGH : 0;
fileFlags |= disableOsCache ? FILE_FLAG_NO_BUFFERING : 0;
fileFlags |= randomAccess ? FILE_FLAG_RANDOM_ACCESS : 0;
fileFlags |= sequentalScan ? FILE_FLAG_SEQUENTIAL_SCAN : 0;
fileFlags |= !fileFlags ? FILE_ATTRIBUTE_NORMAL : 0;
fileFlags |= overlap ? FILE_FLAG_OVERLAPPED : 0;

HANDLE hOutputFile = CreateFile(
    fileName,
    desiredAccess,
    0,
    NULL,
    OPEN_EXISTING,
    fileFlags,
    NULL);

ファイルの読み取り:

_UINT64 CUniformDiskInterface::ReadFromFile(OS_FILE_HANDLE hFile, void *outData, _UINT64 bytesToRead, OVERLAPPED *overlapped, LPOVERLAPPED_COMPLETION_ROUTINE completionRoutine) {
DWORD wBytesRead = 0;

BOOL result = completionRoutine ? 
    ReadFileEx(hFile, outData, (DWORD)(bytesToRead), overlapped, completionRoutine) : 
    ReadFile(hFile, outData, (DWORD)(bytesToRead), &wBytesRead, overlapped);

if (!result)
{
    int errorCode = GetLastError();
    if (errorCode != ERROR_IO_PENDING )
    {
        wstringstream err(wstringstream::in | wstringstream::out);
        err << L"Can't read sectors from file. [ReadFile] error #" << errorCode << L".";
        throw new FileIOException(L"CUniformDiskInterface", L"ReadFromFile", err.str().c_str(), GETDATE, GETFILE, GETLINE); 
    }
}

return (_UINT64)wBytesRead; }

拡張オーバーラップ構造:

            /*!
        \enum EOverlappedStates
        \brief The different overlapped states
        \details Used as inter-thread communication while waiting for the I/O operation to complete 
        */
        enum EOverlappedStates
        {
            /** The I/O operation has not started or in in-progress */
            EOverlappedNotStarted, 

            /** The I/O operation is done and was successful */
            EOverlappedSuccess, 

            /** The I/O operation is done but there was an error */
            EOverlappedError
        };

        /*!
        \struct OverlappedEx
        \brief Extended overlapped structure
        */
        struct OverlappedEx : OVERLAPPED
        {           
            /** The buffer manager that is designated to cache the record when it's loaded */
            CBufferManager *bufferManger;

            /** Transaction ID related to this disk I/O operation */
            _UINT64 transactionId;

            /** Start disk sector of the record */
            _UINT64 startDiskSector;

            /** Buffer */
            void *buffer;

            /** Number of bytes in \c buffer */
            _UINT64 bufferSize;

            /** Current overlapped I/O state. Used for inter-thread communication while waiting for the I/O to complete */
            EOverlappedStates state;

            /** Error code, or \c 0 if no error */
            _UINT32 errorCode;
        };

コールバックメソッド:

    /*! \brief Callback routine after a overlapped read has completed
\details Fills the buffer managers buffer cache with the read data
\todo This callback method may be a bottleneck, so look into how to handle this better
*/
VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap) 
{ 
    OverlappedEx *overlapped = (OverlappedEx*)lpOverLap;
    overlapped->errorCode = (_UINT32)dwErr;

    if (!dwErr && cbBytesRead) 
    { 
        overlapped->state = EOverlappedSuccess;
        overlapped->bufferManger->AddBuffer(overlapped->startDiskSector, overlapped->buffer, overlapped->bufferSize);
    }
    else
    {
        // An error occurred
        overlapped->state = EOverlappedError;
    }
} 

使用法:

    _UINT64 startDiskSector = location / sectorByteSize;
void *buffer = bufferManager->GetBuffer(startDiskSector);
if (!buffer)
{
    /*
    The disk sector was not cached, so get the data from the disk and cache in internal memory with
    the buffer manager
    */
    buffer = new char[recordByteSize];

    // Create a overlapped structure to enable disk async I/O operations
    OverlappedEx *overlapped = new OverlappedEx;
    memset(overlapped, 0, sizeof(OverlappedEx));
    overlapped->Offset = (DWORD)(startDiskSector & 0xffffffffULL);
    overlapped->OffsetHigh = (DWORD)(startDiskSector >> 31ULL);
    overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    overlapped->bufferManger = bufferManager;
    overlapped->startDiskSector = startDiskSector;
    overlapped->buffer = buffer;
    overlapped->bufferSize = recordByteSize;
    overlapped->state = EOverlappedNotStarted;

    // Read from disk
    diskApi.ReadFromFile(fileHandle, buffer, sectorByteSize, overlapped, CompletedReadRoutine);
    return overlapped;
}
4

1 に答える 1

1

MSDNのドキュメントによると、コールバック関数は、関数を呼び出したのと同じスレッドでReadFileEx、スレッドがイベントの発生を待機しているときにのみ呼び出されます。したがって、コールバックの呼び出しとコールバックの呼び出しの間に同期の問題がないことが保証されReadFileExます。

OverlappedExこれは、 1つのスレッドだけがその構造の特定のインスタンスに読み込もうとする限り、データ構造へのアクセスを同期する必要がないことを意味します。これは、1つのスレッドからのみ特定のファイルを読み取ることと同じです。複数のスレッドから単一のファイルを読み取ろうとすると、Windows自体で問題が発生する可能性があります(非同期I / O自体はスレッドセーフではないと思います)。したがって、ミューテックスをロックしても効果はありません。その場合。

于 2012-12-26T15:41:15.040 に答える