Tempo che limita un metodo in C #

Ho una struttura di gioco in cui c’è una lista di robot che implementano IBotInterface. Questi robot sono personalizzati dall’utente con l’unica limitazione che devono implementare l’interfaccia.

Il gioco chiama quindi i metodi nei robot (si spera in parallelo) per vari eventi come YourTurn e roundStart. Voglio che il bot impieghi solo una quantità limitata di tempo per gestire questi eventi prima di essere costretto a lasciare il computing.

Un esempio del tipo di cosa che sto provando è: (dove NewGame è un delegato)

Parallel.ForEach(Bots, delegate(IBot bot) { NewGame del = bot.NewGame; IAsyncResult r = del.BeginInvoke(Info, null, null); WaitHandle h = r.AsyncWaitHandle; h.WaitOne(RoundLimit); if (!r.IsCompleted) { del.EndInvoke(r); } } ); 

In questo caso sono costretto a eseguire EndInvoke () che potrebbe non terminare. Non riesco a pensare a un modo per annullare in modo pulito il thread.

Sarebbe bello se ci fosse un qualche tipo di

 try { bot.NewGame(Info); } catch (TimeOutException) { // Tell bot off. } finally { // Compute things. } 

Ma non penso che sia ansible fare un tale costrutto.

Lo scopo di questo è di gestire con grazia le IA che hanno cicli infiniti accidentali o che impiegano molto tempo a calcolare.

Un altro modo ansible per affrontarlo sarebbe avere qualcosa di simile (con più c # e meno pseudocodice)

 Class ActionThread { pulbic Thread thread { get; set; } public Queue queue { get; set; } public void Run() { while (true) { queue.WaitOne(); Act a = queue.dequeue(); a(); } } Class foo { main() { .... foreach(Bot b in Bots) { ActionThread a = getActionThread(b.UniqueID); NewGame del = b.NewGame; a.queue.queue(del); } Thread.Sleep(1000); foreach (ActionThread a in Threads) { a.Suspend(); } } } 

Non è il modo più pulito ma funzionerebbe. (Mi preoccuperò di come passare i parametri e prendere i valori di ritorno in seguito).

[Ulteriori informazioni]

Non sono sicuro di cosa sia un appdomain, dal suo aspetto potrei farlo, ma non vedo come possa essere d’aiuto

Spero di non aspettarmi codice maligno. Cercare di uccidere i thread degli altri robot non è un modo valido per vincere la partita. Volevo solo dare a ogni bot un secondo per calcolare poi andare avanti con il stream di gioco, quindi prevedo principalmente codice lento o bugged qui.

Sto provando a vedere cosa posso fare con Task, arrivare lentamente da qualche parte.

Leggerò in che cosa CAS può fare, grazie ragazzi

[Altro Modifica]

Mi fa male la testa, non riesco a pensare o codificare più. Sto configurando un sistema di trasmissione dei messaggi su un thread dedicato per bot e sospenderò / dormirò quei thread

Ho deciso che userò un sistema client server con socket completo. In modo che il client possa fare tutto ciò che vuole e io semplicemente ignorerò se rifiuta di rispondere ai messaggi del server. Peccato che ha dovuto venire a questo.

    Sfortunatamente non esiste un modo sicuro al 100% di terminare un thread in modo pulito , come sembra tu voglia.

    Ci sono molti modi in cui puoi provare a farlo, ma tutti hanno alcuni effetti collaterali e svantaggi che potresti voler prendere in considerazione.

    L’unico modo pulito, sicuro e approvato per farlo è quello di ottenere la collaborazione del thread in questione e chiederglielo gentilmente . Tuttavia, questo è solo un modo garantito al 100% se hai il controllo del codice. Dal momento che non lo sei, non lo sarà.

    Ecco il problema

    • Se fai un Thread.Abort , rischi di lasciare l’appdomain in uno stato non sicuro. Ci possono essere file lasciati aperti, connessioni di rete o database lasciate aperte, oggetti del kernel lasciati in uno stato non valido, ecc.
    • Anche se inserisci il thread nel proprio appdomain e abbatti l’appdomain dopo aver interrotto il thread, non puoi essere sicuro al 100% che il tuo processo non avrà problemi in futuro a causa di esso.

    Diamo un’occhiata al motivo per cui la cooperazione non sarà neanche al 100%.

    Diciamo che il thread in questione ha spesso bisogno di chiamare il codice della libreria, per disegnare sullo schermo, o cosa no. Puoi facilmente inserire un controllo in questi metodi e generare un’eccezione.

    Tuttavia, tale eccezione potrebbe essere catturata e ingerita.

    Oppure il codice in questione potrebbe entrare in un ciclo infinito che non chiama affatto il codice della libreria, il che ti riporta al punto di partenza, come uccidere il thread senza la sua cooperazione.

    Che ho già detto che non puoi.

    C’è, tuttavia, un metodo che potrebbe funzionare. È ansible generare il bot nel proprio processo, quindi interrompere il processo quando scade. Questo ti darebbe una maggiore possibilità di successo perché almeno il sistema operativo si prenderà cura di tutte le risorse che gestisce quando il processo muore. Ovviamente è ansible che il processo lasci i file danneggiati nel sistema, quindi, di nuovo, non è pulito al 100%.

    Ecco un post di blog di Joe Duffy che spiega molto su questi problemi: codice gestito e hardening asincrono .

    Un’attività .NET 4.0 offre una notevole flessibilità per quanto riguarda l’annullamento.

    Esegui il delegato in un’attività in cui hai CancelationToken un CancelationToken , come questo .

    C’è un esempio più completo qui .

    modificare

    Alla luce del feedback, credo che la risposta giusta sia seguire questo piano:

    1. Limita l’intelligenza artificiale utilizzando CAS, in modo che non possa accedere ai primitivi di threading o fare confusione con i file.

    2. Dagli un richiamo di battito cardiaco che è moralmente obbligato a chiamare dall’interno di loop lunghi. Questa callback genererà un’eccezione se il thread ha impiegato troppo tempo.

    3. Se l’AI scade, registra una mossa debole e concedilo per un breve periodo di tempo per chiamare quella richiamata. In caso contrario, basta mettere il filo in sospeso fino alla sua prossima svolta. Se non lo fa mai, continuerà a registrare le mosse deboli finché il processo non lo ucciderà per essere un thread in background.

    4. Profitto!

    Un’opzione è sicuramente quella di creare una nuova discussione autonomamente, piuttosto che affidarsi a BeginInvoke. Puoi implementare il timeout tramite Thread.Join e (senza tanti complimenti) uccidere il thread, se necessario, tramite Thread.Abort.

    Come ho accennato nella risposta di @Lasse, dovresti davvero considerare l’utilizzo di Code Access Security per limitare ciò che i bot sono autorizzati a fare sul sistema. Puoi davvero limitare ciò che i bot sono autorizzati a fare in termini di esecuzione (inclusa la rimozione della loro capacità di accedere alle API di threading). Potrebbe valere la pena considerare di trattare ogni bot come se si trattasse di un virus poiché non si ha il controllo di ciò che potrebbe fare sul proprio sistema e presumo che il gioco verrà eseguito come applicazione a pieno titolo.

    Detto questo, se si limita ciò che ogni bot fa con CAS, si può essere in grado di terminare il thread senza timore di corrompere il sistema. Se un bot è bloccato in un ciclo infinito, sospetto che la tua unica risorsa sarà quella di terminare il thread dal momento che, programmaticamente, non sarà mai in grado di raggiungere alcun codice che controlli un segnale Thread.Abort.

    Questo articolo MSDN potrebbe fornire alcune indicazioni su come terminare in modo più efficace un thread run-away utilizzando una funzione TerminateThread.

    Devi davvero provare tutte queste cose e provare a te stesso quale potrebbe essere la soluzione migliore.

    In alternativa, se questo è un gioco competitivo e gli autori di bot devono giocare onestamente, hai considerato che il gioco conferisca a ogni bot un “turno token” che il bot deve presentare con ogni azione che vuole invocare e quando il gioco si gira , dà a tutti i robot un nuovo token. Questo potrebbe consentire ora solo di preoccuparsi dei thread run-away e non dei robot che prendono a lungo il loro turno.

    modificare

    Solo per aggiungere un posto dove le persone possano andare. Le Enumerazioni di Flag di Permesso di Sicurezza ti daranno un’idea di dove cominciare. Se rimuovi l’authorization SecurityPermissionFlag.ControlThread (ad esempio, solo dando il permesso del codice per eseguire ed escludendo ControlThread) rimuovi i loro

    Possibilità di utilizzare determinate operazioni avanzate sui thread.

    Non conosco l’estensione delle operazioni che sono inaccessibili, ma sarà un esercizio interessante per il weekend per capirlo.

    Penso che l’unico problema sia un condizionale invertito. È necessario chiamare EndInvoke solo se l’attività è stata completata correttamente.

    Suggerimento approssimativo:

     var goodBots = new List(); var results = new IAsyncResult[Bots.Count]; var events = new WaitHandle[Bots.Count]; int i = 0; foreach (IBot bot in Bots) { NewGame del = bot.NewGame; results[i] = del.BeginInvoke(Info, null, null); events[i++] = r.AsyncWaitHandle; } WaitAll(events, RoundLimit); var goodBots = new List(); for( i = 0; i < events.Count; i++ ) { if (results[i].IsCompleted) { goodBots.Add(Bots[i]); EndInvoke(results[i]); results[i].Dispose(); } else { WriteLine("bot " + i.ToString() + " eliminated by timeout."); } } Bots = goodBots; 

    Sarebbe anche meglio far girare esplicitamente un thread per ogni bot, in questo modo si potrebbero sospendere i robot con comportamento anomalo (o comporre la loro priorità) in modo che non continuino a rubare il tempo della CPU ai buoni robot.

    un’altra opzione progettuale potrebbe essere quella di consentire a ciascun bot di essere il proprio processo, quindi utilizzare un meccanismo IPC per lo scambio di dati. Di solito è ansible interrompere un processo senza ripercussioni terribili.