Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 16
  1. #1
    Utente di HTML.it
    Registrato dal
    Mar 2001
    Messaggi
    577

    [C++] risorse condivise thread

    ciao,
    mi stavo chiedendo se nel momento in cui viene richiamata la funzione scrivifile() da parte di uno dei thread, era sufficiente racchiudere la chiamata scrivifile() tra

    codice:
    EnterCriticalSection(&CSector);
    scrivifile();
    LeaveCriticalSection(&CSector);
    per evitare che gli n thread usino contemporanemanete la medesima funzione oppure si deve scrivere direttamente le chiamate alla funzione critica nella funzione stessa.
    Nel caso sopra sarebbe piuttosto comodo.

    codice:
    //*  Use the -tWM  (32-bit multi-threaded target) command-line switch for this example  */
    #pragma checkoption -tWM
    #include        <windows.h>
    #include        <process.h>
    #include        <stdio.h>
    
    #define NTHREADS 25
    
    CRITICAL_SECTION CSector;
    
    /* This function acts as the 'main' function for each new thread */
    static unsigned __stdcall threadMain(void *arg)
    { 
         
       EnterCriticalSection(&CSector);
       scrivifile();
       LeaveCriticalSection(&CSector);
       
       return 0;
    }
    
    int main(void)
    {
       HANDLE hThreads[NTHREADS];
    
       int i;
       unsigned threadId;
       SECURITY_ATTRIBUTES  sa = {
              sizeof(SECURITY_ATTRIBUTES), /* structure size */
              0,      /* No security descriptor */
              TRUE    /* Thread handle is inheritable */
       };
    
       /* Create NTHREADS inheritable threads that are initially suspended and that will run starting at threadMain()*/
       for(i = 0; i < NTHREADS; i++) {
    
          hThreads[i] = (HANDLE)_beginthreadex(
             &sa,          /* Thread security */
    
             4096,         /* Thread stack size */
             threadMain,   /* Thread starting address */
             (void *)i,    /* Thread start argument */
             CREATE_SUSPENDED,  /* Create in suspended state */
             &threadId);   /* Thread ID */
          if(hThreads[i] == INVALID_HANDLE_VALUE) {
             MessageBox(0, "Thread Creation Failed", "Error", MB_OK);
             return  1;
          }
          printf("Created thread %2d with an ID of %u\n", i, threadId);
       }
       printf("\nPress ENTER to thaw all threads\n\n");
    
       getchar();
       
    /* Resume the suspended threads */
       for(i = 0; i < NTHREADS; i++)
          ResumeThread(hThreads[i]);
    
    InitializeCriticalSection(&CSector);
    
       
    /* Wait for the threads to run */
       WaitForMultipleObjects(NTHREADS, hThreads, TRUE, INFINITE);
    
    /* Close all of the thread handles */
       for(i = 0; i < NTHREADS; i++)
          CloseHandle(hThreads[i]);
    
    DeleteCriticalSection(&CSector);
    
       return 0;
    }
    
    /* ----------------------------------------------- */
    void scrivifile()
    {
       FILE *fp;
       fp=fopen();
       fprintf("test.txt","w");
       fclose(fp);
    }

  2. #2
    Una critical section concettualmente non protegge una funzione, ma dei dati. Se la funzione opera implicitamente su dei dati globali, allora la critical section normalmente starà nella funzione.
    Ultima modifica di MItaly; 11-12-2014 a 22:50
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it
    Registrato dal
    Mar 2001
    Messaggi
    577
    credo di aver intuito allora che l'esempio da me postato non va bene per generare una condizione di race condition.

  4. #4
    Nel tuo caso non ci sarebbe nessuna race condition anche senza critical section, visto che non c'è sharing di niente tra i vari thread.
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it
    Registrato dal
    Mar 2001
    Messaggi
    577
    se il puntatore fp lo dichiarassi globale e trasformassi la funzione scrivifile() semplicemente così?

    codice:
     void scrivifile()
     {  
          fprintf(fp, "test.txt","w"); 
     }
    non si avrebbe race condition per usare la funzione?

    Pensavo: ad un thread viene data la cpu ed esegue un paio di istuzioni, scade il suo quato di tempo e il thread successivo scrive un qualche carattere su file; no fa in tempo a scrivere tutto l'output che scade il suo quanto di tempo e così di seguito.
    Credo che dovrei trovarmi alla fine un file un po' bizzarro o sbaglio?
    Ultima modifica di misterx; 12-12-2014 a 00:00

  6. #6
    Dipende dell'implementazione di fprintf... Che io sappia sia glibc che la CRT Microsoft hanno un lock gestito automaticamente nelle operazioni su FILE *, per cui non dovrebbe essere necessario, in caso contrario effettivamente la critical section servirebbe.

    Riguardo alla versione precedente, mi sono dimenticato di dire che è vero che non c'è rischio di stato incoerente a livello di strutture dati del tuo programma, ma visto che tutti pasticciano lo stesso file in contemporanea il suo contenuto in un dato momento non è deterministico (a seconda di come vengono ordinate le fopen, le scritture dei buffer, eccetera). In questo la situazione è perfettamente analoga all'avere più processi che lavorano insieme sullo stesso file senza sincronizzazione.
    Amaro C++, il gusto pieno dell'undefined behavior.

  7. #7
    Utente di HTML.it
    Registrato dal
    Mar 2001
    Messaggi
    577
    quindi in definitiva, con o senza CriticalSection potrei ritrovarmi alla fine un file "bizzarro" in quanto non è detto che ogni thread riesce a scrivere sempre tutti i dati completi nel momento in cui ottiene la cpu.
    Se così fosse la soluzione migliore sarebbe un file per thread?

    Quella del lock automatico non la sapevo.

  8. #8
    No 'spe, non facciamo confusione.

    Caso 1: apertura/scrittura/chiusura con critical section globale.
    codice:
    void scrivifile()
    {
        EnterCriticalSection(&CSector);
        FILE *fp;
        fp=fopen("test.txt","w");
        fprintf(fp, "hello world");
        fclose(fp);
        LeaveCriticalSection(&CSector);
    }
    L'operazione di apri-scrivi-chiudi avviene sempre insieme e solo un thread alla volta; guardando "da fuori", vedrai sempre un file o vuoto, o con "hello world" o con un qualunque stato intermedio della scrittura (a seconda del buffering).

    Caso 2, come sopra ma senza critical section: ogni thread apre il suo FILE *, per cui non si "pestano i piedi" a livello di strutture dati lato libreria C, però le scritture arrivano al sistema operativo a caso, per cui nel file potresti avere più o meno qualunque mix delle varie scritture.

    Caso 3, FILE * aperto una sola volta all'inizio e tutti fanno fprintf sullo stesso FILE *: dipende da come è implementata la libreria C.
    - se la libreria è conforme allo standard C11 (cosa che comunque facevano già da prima la maggior parte delle librerie C che supportavano i thread), allora tutto fila liscio, dato che ogni stream C ha associato un lock che viene acquisito internamente all'inizio della fprintf e rilasciato alla fine. Le singole fprintf avvengono in maniera atomica, e, dato che al sistema operativo arriva tutto dallo stesso stream, non c'è il casino di cui al caso 2.
    - in caso contrario (caso classico: si è linkata una versione della CRT pensata per lavorare solo single thread) se non c'è una critical section può succedere di tutto, molto probabilmente un segfault, dato che da più thread si accede alla struttura dati puntata da fp senza sincronizzazione, per cui è facile che rapidamente si venga a trovare in uno stato incoerente.

    In ogni caso, il caso dei file non è una buona palestra per parlare di thread safety, dato che è ingarbugliato ulteriormente dal fatto che sono oggetti gestiti internamente dalla CRT (e su cui prima del C11 non c'erano garanzie) e che wrappano internamente una risorsa che può essere condivisa ad un livello più basso (sistema operativo). In genere è meno distraente ragionare su strutture dati condivise tra thread in memoria (vengono a c'entrare meno fattori "distraenti").
    Ultima modifica di MItaly; 12-12-2014 a 00:53
    Amaro C++, il gusto pieno dell'undefined behavior.

  9. #9
    Utente di HTML.it
    Registrato dal
    Mar 2001
    Messaggi
    577
    però, permettimi una considerazione e magari sbaglio: il thread1 apre il file quindi fp è assegnato e il file risulta bloccato; viene data la CPU al secondo thread2 il quale apre ovviamente il medesimo file nella stessa posizione: non dovrebbe bloccarsi tutto con un errore di violazione?

  10. #10
    Perché? O il file viene aperto in modalità esclusiva (nel qual caso fallisce la seconda fopen), oppure il sistema operativo non ha particolari problemi a gestire scritture contemporanee (il caso di scritture da più thread è sostanzialmente equivalente alla scrittura da più processi); ovviamente poi i risultati saranno quantomeno bizzarri (ma di nuovo, la questione con i file è particolarmente complicata perché dipende da cosa combina sotto il sistema operativo).
    Amaro C++, il gusto pieno dell'undefined behavior.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.