Domanda

Sto correndo in una situazione in cui ho bisogno atomica somma di due valori in memoria.Il codice che ho ereditato va come questa:

int a = *MemoryLocationOne;
memory_fence();
int b = *MemoryLocationTwo;
return (a + b) == 0;

Le letture di a e b sono atomica, e tutte le scrive altrove nel codice a queste due posizioni di memoria sono anche lockless atomica.Tuttavia il problema è che i valori delle due posizioni possono cambiare tra le due letture.

Allora, come faccio a fare questa operazione atomica?So tutto del CAS, ma tende solo richiedono la realizzazione di read-modify-write operazioni atomiche e che non è abbastanza quello che voglio fare qui.

C'è un modo per farlo, o è l'opzione migliore per il refactoring del codice, in modo che solo bisogno di controllare un valore?

Edit:Grazie, non ho detto che volevo fare questo locklessly nella prima revisione, ma alcune persone hanno notato su di esso dopo la mia seconda revisione.So che nessuno è convinto che le persone quando dicono cose come questa, ma non riesco a utilizzare il blocca praticamente.Mi piacerebbe emulare un mutex con atomics e che sarebbe più lavoro di refactoring del codice per tenere traccia di un valore invece di due.

Per ora il mio metodo di indagine coinvolge approfittando del fatto che i valori sono consecutivi e li afferra atomicamente con una versione a 64 bit di lettura, che sono certo sono atomica sul mio piattaforme di destinazione.Se qualcuno ha idee nuove, si prega di contribuire!Grazie.

È stato utile?

Soluzione

Se avete davvero bisogno di assicurarsi che a e b non cambia mentre si sta facendo questo test, quindi è necessario utilizzare la stessa per la sincronizzazione tutti accesso a a e b.Questa è la tua unica scelta.Ogni operazione di lettura e scrittura su di uno di questi valori è necessario utilizzare la stessa recinzione, sincronizzatore, semaforo, timeslice di blocco, o qualunque sia il meccanismo utilizzato.

Con questo, è possibile garantire che se si:

memory_fence_start();
int a = *MemoryLocationOne;
int b = *MemoryLocationTwo;
int test = (a + b) == 0;
memory_fence_stop();

return test;

quindi a non cambia durante la lettura b.Ma, ancora una volta, è necessario utilizzare lo stesso meccanismo di sincronizzazione per tutti accesso a a e per b.

Per riflettere un secondo di modifica alla tua domanda che siete alla ricerca di un lock-free metodo, beh, dipende dal processore utilizzato e per quanto tempo a e b sono, e se queste locazioni di memoria consecutive e allineati correttamente.

Supponendo che questi sono consecutive in memoria e 32 bit ciascuno e che il tuo processore ha un atomic 64-bit, quindi è possibile l'emissione atomica a 64 bit di lettura per leggere i due valori, analizzare i due valori al di fuori del valore a 64 bit, fare la matematica e restituire ciò che si desidera tornare.Supponendo di non aver mai bisogno di un aggiornamento atomico a "a e b allo stesso tempo," ma solo atomica aggiornamenti "a"o a "b"in isolamento, quindi questo farà quello che vuole, senza blocchi.

Altri suggerimenti

Si dovrebbe fare in modo che tutto il mondo uno dei due valori sono stati letti o scritti, sono stati circondati da una barriera di memoria (blocco o sezione critica).

// all reads...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    a = *MemoryLocationOne;
    b = *MemoryLocationTwo;
}

...

// all writes...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    *MemoryLocationOne = someValue;
    *MemoryLocationTwo = someOtherValue;
}

Se si prendono di mira 86, è possibile utilizzare il supporto a 64 bit confrontare / scambio e confezionare entrambi int di in una sola parola a 64 bit.

In Windows, si dovrebbe fare questo:

// Skipping ensuring padding.
union Data
{
     struct members
     {
         int a;
         int b;
     };

     LONGLONG _64bitData;  
};

Data* data;


Data captured;

do
{
    captured = *data;
    int result = captured.members.a + captured.members.b;
} while (InterlockedCompareExchange64((LONGLONG*)&data->_64bitData,
                    captured._64BitData,
                    captured._64bitData) != captured._64BitData);

davvero brutto. Io suggerirei di usare un blocco -. Molto più gestibile

EDIT: Per aggiornare e leggere le singole parti:

data->members.a = 0;
fence();

data->members.b = 0;
fence();

int captured = data->members.a;

int captured = data->members.b;

Non c'è davvero nessun modo per fare questo senza una serratura. Non ci sono i processori hanno una doppia lettura atomica, per quanto ne so.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top