Implementazione della soluzione per il problema di condivisione risorse con multithreading?

2

Sto cercando di capire un'implementazione per una soluzione a un problema che ho delineato in un'altra domanda Programmers.SE . Ho bisogno di trovare una soluzione per questo problema in C # molto probabilmente usando la collezione nello spazio dei nomi System.Collections.Concurrent .

Il mio problema è un classico problema relativo al Problema dei filosofi in sala . Ho un insieme finito di risorse che devono essere utilizzate da più thread. La mia attuale implementazione di una soluzione implica una sorta di sistema di segnalazione per far sapere agli altri thread quando possono iniziare a usare una risorsa. In realtà ho creato una bella analogia.

Le risorse sono viste come giostre dei parchi di divertimento che possono essere adattate a un solo pilota alla volta. Thread / compiti / unità di lavoro sono visti come gruppi di persone che desiderano guidare più corse diverse allo stesso tempo.

Quando un gruppo di persone entra nel parco si fermano allo stand di biglietteria, ognuno acquista un biglietto per il rispettivo giro e si mette in fila per le rispettive linee, tutte simultaneamente . Ognuno di questi gruppi di persone comincerà a cavalcare solo una volta che saranno tutti in prima linea, altrimenti attenderanno i primi in fila alle rispettive corse fino a quando tutti saranno in prima fila.

Questa soluzione funzionerebbe ma la raccolta simultanea in C # rende difficile l'implementazione corretta. (Andando avanti con l'analogia) Come risultato di ogni persona del gruppo che deve acquistare i biglietti e mettersi in fila tutti contemporaneamente, a un certo punto devo fare atomicamente più modifiche ad alcune raccolte simultanee. In C # non esiste un meccanismo per farlo in System.Collections.Concurrent .

In C # ho le seguenti strutture dati per rappresentare l'analogia sopra

ConcurrentDictionary<IResource, ConcurrentQueue<Guid>> ResourceWaitQueues { get; set; }
ConcurrentDictionary<ITask, ConcurrentDictionary<IResource, Guid>> TasksToDo { get; set; } 

[Passaggio 1.a] quando viene generata una nuova ITask , viene interrogata per IResource s che deve essere eseguita. Per ogni IResource è necessario un nuovo Guid generato. A KeyValuePair viene creato per questo ITask e inserito in TasksToDo .

[Passaggio 1.b] Il Guid creato per ogni IResource il ITask necessario viene quindi inserito nel corrispondente ConcurrentQueue<Guid> nell'oggetto ResourcesWaitQueues .

[DispatchThread] Il thread di dispatch (TPL Task ) viene avviato. Questo thread scorre continuamente sulla struttura TasksToDo e controlla se il ITask corrente di List<Guid> necessario per l'esecuzione si trova nella parte anteriore di ConcurrentQueue s in ResourceWaitQueues . Se è così, viene avviato, altrimenti viene saltato.

[TaskRunningThread] quando viene trovato un ITask pronto per essere eseguito, viene eseguito. Una volta completato, il List<Guid> in attesa viene rimosso dalla rispettiva percentuale% di% in ConcurrentQueue .

Come dovresti notare, il problema con questa soluzione è che devo aggiungere più o rimuovere ResourceWaitQueues e ConcurrentQueue quando viene generato un nuovo ConcurrentDictionary o quando è stato completato un ITask . Se DispatchThread o TaskRunningThread riesce a intercalarsi tra loro, è possibile che ITask non venga mai aggiunto o rimosso dalle rispettive raccolte.

Mi piacerebbe pensare di essere abbastanza esperto da scrivere la mia collezione simultanea per consentire più modifiche atomiche, anche se preferirei usare le classi ben sviluppate.

Quindi la mia domanda è, c'è un modo per risolvere questo problema con le collezioni Guid fuori dalla lista?

    
posta KDecker 26.08.2016 - 22:11
fonte

1 risposta

1

Probabilmente hai bisogno di un qualche tipo di blocco quando accedi simultaneamente a più code. Se si desidera realmente attenersi a System.Collections.Concurrent, è possibile memorizzare un singolo token in qualsiasi IProducerConsumerCollection . Ma sarebbe piuttosto stupido.

Ho combinato raccolte concorrenti e utilizzato ReaderWriterLockSlim . Quando si esegue una singola operazione su una singola coda come GetorAdd() , vorrei usare EnterReadLock() basato su la sicurezza della filettatura della collezione e consentendo contemporaneamente altre semplici operazioni. Quando eseguo una "transazione" di operazioni su una o più raccolte, utilizzerei EnterWriteLock() segnalazione dell'uso esclusivo di tutte le raccolte simultanee.

    
risposta data 27.08.2016 - 15:56
fonte

Leggi altre domande sui tag