2011-01-19 19 views
9

Questo è un po 'correlato a this question, ma penso che ho bisogno di sapere un po' di più. Ho cercato di capire come farlo per alcuni giorni (mentre lavoravo su altre parti), ma è giunto il momento per me di mordere il proiettile e ottenere multi-thread. Inoltre, sto cercando un po 'più di informazioni rispetto alla domanda collegata.boost :: asio, thread e sincronizzazione

In primo luogo, sulla multi-threading. Come ho provato il mio codice, non mi sono preoccupato di nessun multi-threading. È solo un'applicazione di console che avvia una connessione a un server di test e tutto il resto viene gestito. Il ciclo principale è questo:

while(true) 
{ 
    Root::instance().performIO(); // calls io_service::runOne(); 
} 

Quando scrivo la mia domanda principale, sto cercando di indovinare questa soluzione non sarà accettabile (come sarebbe dovuto essere chiamato nel ciclo di messaggi che, pur possibile, avrebbe problemi quando la coda del messaggio blocca l'attesa di un messaggio. Potresti cambiarlo in modo che il ciclo del messaggio non blocchi, ma non è quello che bloccherà l'utilizzo della CPU attraverso il tetto?)

La soluzione sembra è di lanciarne un altro. Ok bene. Ma poi ho letto che restituisce io_service::run() quando non c'è lavoro da fare. Cos'è quello? È che quando non ci sono dati o connessioni? Se esiste almeno una connessione, rimane in vita? Se è così, non è tanto un problema dato che devo solo avviare un nuovo thread quando viene effettuata la prima connessione e sono felice se tutto si interrompe quando non c'è niente da fare. Immagino di essere confuso dalla definizione di "niente lavoro da fare".

Quindi devo preoccuparmi di sincronizzare il mio boost con il thread principale della GUI. Quindi, suppongo che le mie domande siano:

  1. Qual è il modo migliore di utilizzare boost :: asio in un'applicazione client per quanto riguarda i thread e mantenerli in vita?
  2. Durante la scrittura su un socket dalla filettatura principale alla filettatura IO, la sincronizzazione è stata ottenuta utilizzando boost::asio::post, in modo che la chiamata si verifichi più tardi in io_service?
  3. Quando i dati sono ricevuti, come fanno le persone a riportare i dati sul thread dell'interfaccia utente? In passato, quando usavo le porte di completamento, creavo un evento speciale che poteva postare i dati sul thread principale dell'interfaccia utente usando a :: SendMessage. Non era elegante, ma ha funzionato.

Oggi ne leggerò un po 'di più, ma sarebbe bello farsi notare da qualcuno che lo ha già fatto. La documentazione di Boost :: asio non è eccezionale, e la maggior parte del mio lavoro finora è stato basato su un po 'di documentazione, alcuni tentativi/errori, qualche codice di esempio sul web.

risposta

6

1) Dai un'occhiata allo io_service::work. Finché esiste un oggetto di lavoro io_service :: run non restituirà. Quindi, se inizi a ripulire, distruggi l'oggetto di lavoro, annulla qualsiasi operazione in sospeso, ad esempio un async_read su un socket, attendi l'esecuzione per tornare e ripulire le risorse.

2) io_service :: post esegue in modo asincrono il gestore dato da un thread su cui è in esecuzione il servizio io. È possibile utilizzare una richiamata per ottenere il risultato dell'operazione eseguita.

3) È necessaria una qualche forma di sistema di messaggistica per informare il thread della GUI dei nuovi dati. Ci sono diverse possibilità qui.

Per quanto riguarda il tuo commento sul documentario, io Asio è una delle librerie boost meglio documentate e viene fornito con esempi chiari.

+0

+1 io_service :: il lavoro è il modo in cui risolvo il problema da solo. –

+0

grazie per la tua risposta. Quando dici che ci sono diverse possibilità, quali sono quelle che hai usato in passato? –

+0

@ Moo-Juice, non mi trovavo in una situazione in cui c'era un thread specifico della GUI che doveva gestire i dati. Ho appena usato il meccanismo di callback e gestito i dati da un thread io_service come è arrivato.Qualche forma di coda messaggi che i thread gui leggono potrebbe essere usata per indicare l'arrivo di nuovi dati. O potresti semplicemente usare una variabile condizionale. In realtà dipende dall'applicazione, dal design e dai requisiti dell'utente. –

1

boost::io_service::run() restituirà solo quando non c'è niente da fare, quindi nessuna operazione asincrona è in sospeso, ad es. Accettazione/connessione asincrona, attesa asincrona lettura/scrittura o asincrona. quindi prima di chiamare lo io_service::run() devi prima iniziare qualsiasi operazione asincrona.

Non ce l'ho, hai una console o una GUI? in ogni caso il multithreading sembra un overkill. puoi usare Asio insieme al tuo loop di messaggi. se è una GUI win32 puoi chiamare io_service :: run_one() dal tuo gestore OnIdle(). in caso di applicazione console è possibile impostare deadline_timer che controlla regolarmente (ogni 200 ms?) per l'input dell'utente e usarlo con io_service::run(). tutto in singolo thread per semplificare notevolmente la soluzione

2

1) Qual è il modo migliore-pratica di usare boost :: asio in un'applicazione client per quanto riguarda le discussioni e mantenere in vita?

Come documentation suggests, un pool di thread invocano io_service::run è la più scalabile e semplice da implementare.

2) Quando si scrive ad una presa dalla principale filo al filo IO, viene sincronizzazione ottenuti utilizzando boost :: asio :: post, in modo che la chiamata avviene successivamente nel io_service?

È necessario utilizzare a strand per proteggere tutti i gestori che possono essere richiamati da più thread. Vedi this answer in quanto potrebbe aiutarti, così come questo esempio.

3) Quando i dati vengono ricevuti, in che modo le persone richiamano i dati nel thread dell'interfaccia utente? Nel il passato in cui ho utilizzato le porte di completamento, ho creato un evento speciale che potrebbe postare i dati nel thread principale dell'interfaccia utente utilizzando a :: SendMessage. Non era elegante, ma ha funzionato.

Come di fornire una richiamata nella forma di un boost::function quando si registra un evento asincrono al io_service? Quindi il gestore dell'evento può richiamare la richiamata e aggiornare l'interfaccia utente con i risultati.

+0

grazie per questa informazione. Il problema era che i miei callback venivano chiamati dal thread che chiama 'io_service :: run', che non è il thread dell'interfaccia utente principale. Penso che risolverò questo con un delegato proxy che usa SendMessage per invocare un altro delegato sul thread dell'interfaccia utente. Per quanto riguarda i fili, li ho usati. Tuttavia, ho usato 'io_service :: post (strand_.wrap (' piuttosto che 'strand :: post', non è corretto? –

+0

@ Moo-Juice dipende dal contesto in cui sono utilizzati, sia post che wrap sono molto simili –

+0

beh, io uso ** post ** a livello di socket, ad esempio, quando invio i dati ai post dell'interfaccia pubblica a uno interno che avvia 'async_write_some'. Un'astrazione high-up (che accoda i comandi da trasmettere) utilizza il ** wrap ** sui suoi metodi in quanto non solo può essere pubblicato da qualsiasi thread, ma imposta i propri timeline timer (per evitare che la coda venga inviata troppo velocemente). –

0

Quando i dati sono ricevuti, come fanno le persone a riportare i dati sul thread dell'interfaccia utente? In passato, quando usavo le porte di completamento, creavo un evento speciale che poteva postare i dati sul thread principale dell'interfaccia utente usando a :: SendMessage. Non era elegante, ma funzionava

:: PostMessage potrebbe essere più appropriato.

A meno che tutto funzioni in un thread, questi meccanismi devono essere utilizzati per inviare in modo sicuro eventi al thread dell'interfaccia utente.