2012-12-26 2 views
0

동시성이 걱정되는 콜백을 사용하여 비동기 I/O를 구현했습니다. 나는 항상 동일한 파일과 OS 파일로 작업하고 있기 때문에 여러분에게 경의를 표합니다. 물리적 인 I/O는 근본적으로 동기식 작업입니다. 그런 다음 콜백 메소드에 잠금 메커니즘이 필요하지 않습니다.하지만 잘 될지도 모릅니다. 여기에 잘못 입력 - SO 입력 : o) 읽기 작업이 완료되면 버퍼 캐시에 읽기 데이터를 저장하고 EOverlappedStates 열거 상태를 기반으로 각 겹쳐진 작업에 대한 상태 엔진을 저장하는 버퍼 관리자가 있습니다. "I/O가 시작되지 않음", "성공"및 "오류". 우리와 같은 다중 스레드 프로그램에서 동시성을 보장하기 위해 콜백 메소드를 잠글 필요가 있다고 생각합니까?비동기 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; 
    } 
} 
01 23,516,

사용법 :

_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; 
} 

답변

1

documentation on MSDN에 따르면이 콜백 기능은 이제까지 ReadFileEx 함수를 호출 만 스레드가 발생하는 이벤트를 기다리는 경우 동일한 스레드에서 호출됩니다. 따라서 ReadFileEx에 대한 호출과 콜백 호출 사이에는 동기화 문제가 발생하지 않습니다.

즉, 하나의 스레드가 해당 구조의 특정 인스턴스를 읽으려고 시도하는 한 데이터 구조에 대한 액세스를 동기화 할 필요가 없음을 의미합니다. 이는 한 스레드에서만 특정 파일을 읽는 것과 같습니다. 여러 스레드에서 단일 파일을 읽으려고하면 Windows 자체에서 문제가 발생할 가능성이 있습니다 (비동기 I/O는 스레드 안전성이 있다고 생각하지 않습니다). 따라서 뮤텍스를 잠그면 도움이되지 않습니다 너는 그럴거야.

+0

비동기 I/O가 동일한 스레드에 없다는 것을 알고 있습니다. 코드가 제대로 작동하고 동시성 질문에 대한 피드백 만 찾고 있습니다. 아마도 나는 이것에 대해 명확하지 않았지만 나중에 "overlapped-> state == EOverlappedNotStarted"를 검사하는 동일한 스레드가 "I/O 작업이 완료되었음을 의미하는"/ "is not"일 수 있습니다. –

+0

여러 스레드가 동일한 데이터에 액세스하는 경우 무결성을 보장하기 위해 데이터에 잠금 또는 원자 유형/작업이 필요합니다. –

+0

음, 일반적으로 정확합니다, 바트. 그러나 필자는 필자는 ** 물리적 인 I/O가 필자가 필요로하지 않는 동일한 파일에 접근 할 때 OS로부터 근본적으로 동기화된다는 점과 OS가 이미 "동기 부여" 자연. –