Pattern per il controllo di come viene implementata un'interfaccia

4

Questa è più una curiosità che un requisito del mondo reale. Come esempio per dimostrare cosa intendo per "Controllare come viene implementata un'interfaccia" ..

Dire che volevo implementare un nuovo programmatore di attività, e ho esposto i modi in cui l'utente può accodare un nuovo lavoro. Il lavoro aderisce a qualche interfaccia IWork.DoWork o altro. Come posso garantire che l'utente non generi nuovi thread all'interno della loro implementazione.

Tenete a mente, questo è solo un esempio, in realtà non sto cercando di implementare uno schedulatore di thread. Sto solo cercando modi per controllare come viene implementata un'interfaccia, se possibile. Sono interessato ad esempi da qualsiasi lingua.

Modifica: per chiarire, sto cercando modi per limitare le interfacce al di sopra e al di là di ciò che è normalmente consentito dalle interfacce a livello di funzione. Come implemento IWork.DoAnythingExceptCreateNewThreads ?

    
posta ConditionRacer 29.10.2016 - 22:20
fonte

4 risposte

3

Puoi farlo?

Fornire un'interfaccia software è come offrire un servizio a un cliente. Un cliente può scegliere qualsiasi servizio offerto da chiunque. Ad esempio, se la libreria A fornisce un task scheduler e la libreria B fornisce un modo semplice per creare nuovi thread, un cliente può scegliere di utilizzare entrambi.

Perché il cliente vuole farlo? Posso convincere il cliente a non farlo?

I modi migliori per convincere il cliente a non combinarlo con un altro servizio sono:

  • Offrendo lo stesso servizio (equivalente o anche migliore),
  • Spiega ai tuoi clienti perché è meglio non combinarlo con un altro servizio e spero che capiscano

Che cosa succede se esiste un problema tecnico legittimo che non lo rende una buona idea?

Spiega che la preoccupazione tecnica per i clienti è comprensibile.

Ad esempio, una delle maggiori minacce all'implementazione del pool di thread a dimensione fissa è che una funzione worker può richiamare un Sleep che lega il thread per qualche tempo o un Lock / Acquire (bloccando wait) che può essere sbloccato solo da un altro thread di lavoro. Si consideri un semplice scenario che tutti gli 8 thread di un pool di 8 thread fisso decidono di bloccare. Poiché non ci sono altri thread in grado di eseguire il codice per sbloccarlo, questi 8 thread sono in un inceppamento per sempre.

Pertanto, le implementazioni del pool di thread fisso devono spiegare all'utente che è pericoloso utilizzare qualsiasi cosa possa bloccare all'interno delle funzioni di lavoro. In particolare, è ancora più pericoloso se il codice worker tenta di acquisire qualsiasi lock / monitor che può essere rilasciato solo da un altro thread worker.

Infine, introducete all'utente diverse alternative ai blocchi che si rivelano utili nel carico di lavoro computazionale.

  • Ad esempio, i risultati di ciascun thread di lavoro possono essere temporaneamente memorizzati o aggiunti a un elenco o dizionario simultanei e combinati in un secondo momento.
  • Allo stesso modo, è possibile allocare una matrice abbastanza grande in modo che ogni thread di lavoro possa scrivere risultati su parti non sovrapposte della matrice senza calpestare le rispettive aree di output.
  • I dati immutabili condivisi non necessitano della protezione del lucchetto.
  • E così via. (Esistono white paper da alcune librerie di concorrenza che insegnano le migliori pratiche come queste.)

Che cosa succede se sono davvero interessato a bloccare il codice dell'utente cercando di generare nuovi thread?

Questo rientra nei privilegi limitanti per il codice in esecuzione. I privilegi limitanti richiedono il supporto al runtime dall'ambiente di esecuzione. In altre parole, se l'ambiente di esecuzione non implementa il rilascio dei privilegi durante l'esecuzione del codice, non è possibile implementare questa funzionalità da soli. Nella migliore delle ipotesi puoi mettere della documentazione per persuadere i tuoi utenti perché è una cattiva idea farlo.

In generale, se vuoi togliere la libertà dell'utente di usare un altro servizio, dovrai scoprire che il fornitore di altri servizi (ad es. l'ambiente runtime o il sistema operativo, o un altro software), e poi chiederlo fornitore di bloccare l'accesso (vedi: controllo degli accessi), o in qualche modo interferire con quel servizio (vedere: rifiuto del servizio).

Questa vecchia domanda su StackOverflow potrebbe essere utile. Però non esitare a cercare risposte nuove e più accurate.

Alcuni ambienti di esecuzione forniscono controlli di accesso molto elaborati:
(Non ho familiarità con queste cose, quindi non posso spiegare ulteriormente.)

Infine, puoi eseguire il codice utente all'interno del tuo ambiente, che è diverso da quello su cui il tuo codice viene eseguito. Questo è chiamato Sandbox. Il codice dell'utente è come un prigioniero che vive all'interno di un dungeon creato da te.

    
risposta data 29.10.2016 - 22:54
fonte
1

Un'interfaccia è un contratto. L'implementazione deve rispettare quel contratto se implementa l'interfaccia.

Ma questo è proprio come con i contratti nella vita reale. Hai un contratto e qualcun altro firma quel contratto. Come ti assicuri che rispettino il contratto? Hai intenzione di seguirlo senza sosta e assicurati che lo facciano? per quanto tempo li seguirai ?, ecc.

E proprio come nei contratti di vita reale, se frenate il contratto ci sono conseguenze (pagate le tasse extra, pagate le multe, andate in prigione, ecc.)

La risposta è che non puoi. L'implementazione può essere qualsiasi cosa la lingua ti permetta di fare. Spetta allo sviluppatore dell'implementazione rispettare effettivamente il contratto.

Se vuoi avere un qualche tipo di controllo, o limitare l'implementazione, o impostare una direzione in cui dovrebbe muoversi, forse usa una classe astratta con un metodo template invece di un'interfaccia.

    
risposta data 29.10.2016 - 22:43
fonte
1

Credo che questo sia possibile in Haskell. In Haskell, generare un nuovo thread è una funzione IO. Se fornisci un'interfaccia sotto forma di una funzione che prende come input una funzione fornita dall'utente (la funzione "do work"), e non dichiari il tipo di questa funzione di input come un tipo IO, allora l'utente è impedito di generare thread (o qualsiasi altra operazione IO) all'interno della funzione worker. Un monod personalizzato ti permetterebbe di controllare con precisione quali operazioni di I / O sarebbero possibili all'interno della funzione fornita.

Non conosco alcun linguaggio OOP che supporti tuttavia vincoli simili, ma Haskell mostra che è almeno teoricamente possibile.

    
risposta data 30.10.2016 - 14:08
fonte
0

Quello che stai cercando si chiama " Design per contratto ". Questo approccio estende la parte sintattica di una definizione di interfaccia a una parte semantica, utilizzando precondizioni, postcondizioni e invarianti.

Per il tuo esempio, l'"invariante" che stai cercando è il numero di diversi thread dietro "IWork.DoWork", che dovrebbe essere sempre uno. Certo, questo è un po 'troppo semplificato, ma credo che tu abbia un'idea generale.

Come puoi vedere guardando l'articolo di Wikipedia, il supporto del linguaggio di programmazione per DBC differisce da lingua a lingua. Alcune lingue consentono di aggiungere contratti formali alle definizioni dell'interfaccia, almeno in una certa misura. Alcune altre lingue consentono in modo nativo di specificare solo i contratti come parte della documentazione dell'interfaccia informativa.

    
risposta data 30.10.2016 - 00:29
fonte

Leggi altre domande sui tag