I have a set of jobs that I need executed.
eccellente. Hai scelto un'architettura ragionevole per soddisfare questa esigenza.
When a job is finished, the job uses a callback function to inform the entity of the result of the job (Whether it threw an exception and such)
Congratulazioni, hai reinventato Task<T>
e lo hai rinominato Job
. Fare la stessa cosa di una libreria consolidata è la prova che sei sulla strada giusta, ma è anche la prova che potresti ripetere il lavoro che è già stato fatto.
Forse basta usare Task<T>
, se soddisfa le tue esigenze. Tieni presente che un'attività ha una funzione "continua con" in cui puoi assegnare la richiamata direttamente all'attività, proprio come fa Job
.
Inoltre: C # consente di creare flussi di lavoro asincroni basati sulle attività molto facilmente utilizzando await
. L'asincronia basata sulla callback ti fa girare la logica del programma al suo interno e rende difficile la gestione delle eccezioni. Lascia che il compilatore esegua il sollevamento pesante . Riscriverà i tuoi flussi di lavoro nello stile di passaggio continuo per te e otterrai la gestione delle eccezioni corretta.
If the job failed, jobFinishedCallback in TaskSet will call executeJob again.
Questa è la cosa sbagliata da fare, come hai scoperto.
Does anyone have a good suggestion to avoid this problem?
Non chiamare di nuovo executeJob
. Accoda nuovamente il lavoro nella coda di lavoro e ritorna . In questo modo non solo non ottieni una regressione infinita, ma tratti anche il tentativo abbastanza . Il tentativo dovrebbe aspettare il suo turno; tutti i lavori in coda ora dovrebbero avere la priorità su di esso.
Allo stesso modo, quando un lavoro termina normalmente, non chiamarne direttamente la continuazione; puoi incontrare lo stesso problema. Accoda un nuovo lavoro che chiama la continuazione e dà a quel lavoro una continuazione nulla. E ancora, questo è più giusto; la continuazione dovrebbe aspettare il suo turno, proprio come ogni altro lavoro in coda attende il suo turno.
Più in generale: oggi sarebbe un buon giorno per fare qualche ricerca su compiti / futuri / promesse, programmazione reattiva, sequenze osservabili, trampolini, stile di passaggio continuo, modello di attore e così via. Stai reinventando da zero le tecnologie che gli esperti hanno studiato per decenni, quindi impara dalle loro conoscenze accumulate , piuttosto che le tue prove ed errori. E usa le classi testate, debuggate e ben progettate nella libreria , invece di farle da sola - purché soddisfino le tue esigenze. In caso contrario, si prega di fornire un feedback a Microsoft sul motivo per cui non lo fanno.
Le attività C # usano il concetto di "contesto di attività" per determinare come sono programmate le continuazioni; la tua re-invenzione è fondamentalmente le regole di contesto per le applicazioni GUI. La coda dei messaggi di Windows viene utilizzata come coda di lavoro. Ci sono altre scelte che sono possibili; per esempio, alcuni contesti cattureranno invece un thread di lavoro dal pool ed eseguiranno la continuazione sul thread. Usando le parti già pronte già create puoi sfruttare la flessibilità e la potenza già progettate nel sistema.