2015-04-22 18 views
13

Come si può definire un array globale thread-safe con modifiche minime?Come definire il thread safe array?

Desidero che tutti gli accessi siano eseguiti utilizzando il mutex e il blocco sincronizzato.

Qualcosa di simile a questo come 'T' sarà un certo tipo (si noti che la parola chiave 'sync' non è attualmente definito per quanto ne so):

sync Array!(T) syncvar; 

E ogni accesso ad esso sarà simili a questo:

Mutex __syncvar_mutex; 

    //some func scope.... 
    synchronized(__syncvar_mutex) { /* edits 'syncvar' safely */ } 
+0

Proteggere la matrice stessa è facile. Devi solo creare una struttura wrapper con tutte le funzioni dell'array e gli operatori sovraccarichi e dove tutte le funzioni sono sincronizzate. Il problema è ottenere gli elementi dell'array stesso. Proteggere anche quelli diventa molto più complicato, dal momento che non appena li restituisci da una funzione come 'opIndex', non sono più protetti ... –

+0

Ho pensato che questo fosse ciò che' shared (T []) 'era supposto da fare, ma apparentemente non ... –

+0

@ AdamD.Ruppe Tutto ciò che è realmente condiviso è fare in modo che la variabile sia condivisa tra thread piuttosto che thread-local. Ci sono alcuni casi in cui il compilatore si lamenterà se si tenta di fare cose condivise che è garantito essere un problema (in alcuni casi correlato alle operazioni atomiche IIRC), ma non fa nulla con mutex o sincronizzazione. Dipende da te. Le classi sincronizzate sono il modo consigliato per gestire gli oggetti condivisi, ma non sono completamente implementate (solo funzioni sincronizzate) e qualsiasi cosa sfugga a quella classe non sarà più protetta a prescindere. –

risposta

-1

Avete l'idea giusta. Come array, devi essere in grado di modificare e recuperare informazioni. Suggerisco di dare un'occhiata alle utility read-write mutex e atomic fornite da Phobos. Un'operazione di lettura è abbastanza semplice:

  1. synchronize su mutex.readLock
  2. carico (con atomicLoad)
  3. copiare l'elemento fuori dalla synchronize blocco
  4. ritorno l'elemento copiato

scrittura dovrebbe essere quasi esattamente lo stesso Solo syncronize su mutex.writeLock e fare un'operazione cas o atomicOp.

Si noti che ciò funzionerà solo se si copiano gli elementi nell'array durante una lettura. Se si desidera ottenere un riferimento, è necessario eseguire un'ulteriore sincronizzazione sull'elemento ogni volta che si accede o si modifica.

+0

Mi sono fidato del sistema e sento che mi mancherà di nuovo. Dimmi abbastanza che pensi di meritare la reputazione "+150" con questa risposta? – AnArrayOfFunctions

+0

Funziona? Questo è davvero tutto ciò che conta non è vero? Se pensi che la mia risposta ne valga la pena, allora sì. Hai fatto la domanda. Ti ho dato una risposta Se lo ritieni insufficiente, possiamo vedere come può essere modificato. – Straivers

2

Il mio tentativo ingenuo è stato quello di fare qualcosa di simile:

import std.typecons : Proxy: 

synchronized class Array(T) 
{ 
    static import std.array; 
    private std.array.Array!T data; 
    mixin Proxy!data; 
} 

Purtroppo, non funziona a causa di https://issues.dlang.org/show_bug.cgi?id=14509

Non posso dire che sono molto sorpreso se la manipolazione come automagiche di multi- il threading tramite mutex nascosti è molto unidiomatico nella moderna D e il concetto stesso di classi sincronizzate è in gran parte un relitto di D1 volte.

È possibile implementare la stessa soluzione manualmente, ovviamente, definendo la propria classe SharedArray con tutti i metodi necessari e aggiungendo blocchi all'interno dei metodi prima di chiamare i metodi interni privati ​​di plain Array. Ma presumo che tu voglia qualcosa che funzioni di più fuori dagli schemi.

Impossibile inventare qualcosa di meglio qui e ora (ci penserò di più) ma vale la pena notare che in generale è incoraggiato in D creare strutture dati progettate per gestire l'accesso condiviso in modo esplicito invece di proteggere solo i dati normali strutture con mutex. E, naturalmente, l'approccio più incoraggiato è quello di non condividere i dati con il passare del messaggio.

aggiornerò la risposta se mi viene in mente qualcosa di meglio.

+0

No - Voglio qualcosa che funzioni e non sia troppo lungo. Grazie comunque - gli darò una foto. – AnArrayOfFunctions

0

È possibile racchiudere la matrice all'interno di una struttura che blocca l'accesso all'array quando un thread acquisisce un token e finché non lo rilascia.

L'involucro/armadio:

  • acquire(): viene chiamato in cicli di un filo. Quando restituisce un puntatore, il thread sa che ha il token quando il metodo restituisce un valore non nullo.
  • release(): viene chiamato da una discussione dopo aver elaborato i dati il ​​cui accesso è stato acquisito in precedenza.

.

shared struct Locker(T) 
{ 
    private: 
     T t; 
     size_t token; 
    public: 
     shared(T) * acquire() 
     { 
      if (token) return null; 
      else 
      { 
       import core.atomic; 
       atomicOp!"+="(token, 1); 
       return &t; 
      } 
     } 
     void release() 
     { 
      import core.atomic; 
      atomicOp!"-="(token, 1); 
     } 
} 

e un breve prova:

alias LockedIntArray = Locker!(size_t[]); 
shared LockedIntArray intArr; 

void arrayTask(size_t cnt) 
{ 
    import core.thread, std.random; 

    // ensure the desynchronization of this job. 
    Thread.sleep(dur!"msecs"(uniform(4, 20))); 

    shared(size_t[])* arr = null; 
    // wait for the token 
    while(arr == null) {arr = intArr.acquire;} 

    *arr ~= cnt;  
    import std.stdio; 
    writeln(*arr); 

    // release the token for the waiting threads 
    intArr.release; 
} 

void main(string[] args) 
{ 
    import std.parallelism; 
    foreach(immutable i; 0..16) 
    { 
     auto job = task(&arrayTask, i); 
     job.executeInNewThread(); 
    } 
} 

Con l'inconveniente che ciascun blocco di operazione sulla matrice deve essere circondato da un acquisire/rilascio coppia.

+0

Bene - Potrei ottenere lo stesso usando il blocco 'sincronizzato'. Nessun vantaggio reale qui? – AnArrayOfFunctions

+0

Tutti ti hanno già detto come fare, non c'è un modo semplice (modifiche minime): avvolgere un array in una struttura e sovraccaricare gli operatori che ti interessano.La soluzione di Михаил Страшун andrebbe bene ma, come ha notato, questo non funziona, che è un concetto molto interessante per una soluzione_ BTW. Ti rendi conto che avresti potuto scriverlo (intendo il wrapper) da giorni? Inoltre, non dimenticare che potresti ottenere una risposta migliore sul NG/forum. Non piace a tutti/usa SO. –

0

È abbastanza semplice creare un wrapper attorno all'array che lo renderà thread-safe. Tuttavia, è estremamente difficile creare un array thread-safe che non sia un collo di bottiglia di concorrenza.

La cosa più vicina che viene in mente è la classe di Java CopyOnWriteArrayList, ma anche questo non è l'ideale ...

+0

Lo so, ma non mi interessa perché è esattamente quello che voglio (collo di bottiglia della concorrenza). Tuttavia, mentre faccio scorrere le risposte, non mi sembra abbastanza facile. – AnArrayOfFunctions