2012-12-26 2 views
0

J'ai implémenté des E/S asynchrones avec un rappel où je suis préoccupé par la concurrence. Je conteste à vous que puisque je travaille toujours avec le même fichier et le fichier OS physique E/S est fondamentalement une opération synchrone alors je n'aurai pas besoin d'un mécanisme de verrouillage dans ma méthode de rappel - mais je peux très bien être mauvais ici - entrez SO: o) J'ai un gestionnaire de tampons qui met les données lues dans son cache tampon quand l'opération de lecture est terminée et un moteur d'état pour chaque opération superposée basée sur les états d'énumération EOverlappedStates; "E/S non démarré", "Réussite" et "Erreur". Pensez-vous que j'ai besoin d'un verrouillage dans la méthode de rappel pour assurer la simultanéité dans un programme multithread comme le nôtre?Concurrence de la méthode de rappel des E/S asynchrones

Ouvrir le fichier:

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); 

fichier Lire:

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

Structure chevauchée étendue:

  /*! 
     \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; 
     }; 

méthode de rappel:

/*! \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; 
    } 
} 

Utilisation:

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

Répondre

1

Selon le documentation on MSDN, la fonction de rappel ne jamais être appelé sur le même thread qui a appelé la fonction ReadFileEx et seulement quand le fil est en attente pour les événements de se produire. Donc, il n'y a pas de problèmes de synchronisation garantis entre l'appel à ReadFileEx et l'appel du rappel. Cela signifie qu'il n'est pas nécessaire de synchroniser l'accès à la structure de données OverlappedEx tant qu'un seul thread essaie de lire dans une instance particulière de cette structure, ce qui équivaut à lire un fichier particulier à partir d'un seul thread. Si vous essayez de lire un seul fichier à partir de plusieurs threads, il est probable que vous rencontriez des problèmes dans Windows même (je ne pense pas que les E/S asynchrones sont thread-safe), verrouiller un mutex n'aidera pas vous dans ce cas.

+0

Je suis conscient que les E/S asynchrones ne sont pas sur le même thread - mon code fonctionne bien et je ne cherche que des commentaires sur la question de la concurrence. Peut-être que je n'étais pas clair à ce sujet, mais c'est "peut-être pas"/"n'est pas" le même thread que les contrôles ultérieurs chevauchement-> state == EOverlappedNotStarted qui signifie encore une fois l'opération d'E/S est terminée. –

+0

Si plusieurs threads accèdent aux mêmes données, vous avez besoin de verrous ou de types/opérations atomiques sur les données pour garantir leur intégrité. –

+0

Eh bien, en général, c'est correct, Bart. Mais comme je l'ai écrit je soutiens que puisque ** physique ** I/O est fondamentalement synchrone de l'OS en accédant au même fichier que je n'ai pas besoin et "supplémentaire" de verrouillage puisque l'OS a déjà "pris soin de cela" par son syncronious la nature. –

Questions connexes