Jag utvecklar ett program som kommunicerar med en server via tre olika TCP/IP-portar (kallade Low Level, High Level och Dencety Ratio). Gör så att du har flaggor, en för varje typ av sak som ska avslutas (t.ex. en för den tråd som hämtar data från low level, en som ansluter för low level etc.), när du sedan ska stänga av så markerar du ytterligare en annan flagga, "avstängningsflaggan". Varje tråd som körs har sedan koll på den, och om den flaggan markerar att programmet ska stängas av så slutar de exekvera vad-de-nu-håller-på-med, och när de slutat så markerar de sin egen flagga för att de är klara för avstängning. Huvudtråden väntar in att alla flaggor för trådarna är markerade, och när alla är det så stängs programmet av.Avsluta alla trådar på ett snyggt sätt
Över den första poten (Low Level) sker flera olika typer av kommunikation (typ 1, typ 2 och typ3). Dessa initieras av klockor som lägger dessa typer på kö med olika frekvens. Denna kö betas sedan av i en tråd som utför kommunikationen av de olika typerna i tur och ordning.
Kommunikationen över den andra porten (High Level) startas också med en klocka. Skillnad mot den första porten är att anslutningen över denna porten alltid är uppkopplad.
Kommunikationen över den tredje porten (Dencety Ratio) startas av servern. Denna del av programmet körs därför som en egen tråd och väntar på att servern ska skicka data. Denna anslutning är också hela tiden uppkopplad.
Om kommunikationen över andra eller tredje porten skulle gå ner startas en klocka som regelbundet försöker koppla upp dessa portar.
Som ni märker är det ett antal klockor och trådar och jag har inte riktigt kommit på hur jag snyggt ska få hela programmet att avslutas. Jag försökte först att bara köra dispose på allt, men om programmet just då håller på att utföra en kommunikation kommer det ju uppstå ett exception då den försöker använda objekt som är avslutade.
Jag skickar med psedukod hur prgrammet är uppbyggd. Är det någon som har en smart idé hur jag ska göra för att alla trådar ska avslutas snyggt
main()
{
m_tLowLevelSession = new Thread(new ThreadStart (LowLevelSession));
m_timerMVDLLType2 = new Timer (new TimerCallback(AddType2Session), RateT2);
m_timerMVDLLType3 = new Timer (new TimerCallback(AddType3Session), RateT3);
m_timerReconnectHL = new Timer (new TimerCallback(ReconnectHL), RateReconnect);
m_timerReconnectDR = new Timer (new TimerCallback(ReconnectDR), RateReconnect);
}
Queue m_lowLevelSessionQueue
ManualResetEvent m_lowLevelAvail
//Hantera alla transaktioner av typen Low Level och skicka dem i tur och ordning till servern
LowLevelSession ()
{
while(true)
{
if (m_lowLevelSessionQueue.Count > 0)
{
gwOrder = m_lowLevelSessionQueue.Dequeue();
//Behandla data som ska skickas/tas emot
//Anslut till server via TCP/IP-port för low Level
//Skicka data till servern
SendDataToServer(transmit)
//Ta emot ACK
ReadDataFromServer(ack)
//Skicka Command
SendDataToServer(command)
//Ta emot ACK
ReadDataFromServer(ack)
//Ta emot Echo med samma data som skickades
ReadDataFromServer(echo)
//Meddela servern om överföringarna gick bra
SendDataToServer(ok)
//Ta emot ACK
ReadDataFromServer(ack)
//Skicka Termination
SendDataToServer(termination)
//Ta emot ACK
ReadDataFromServer(ack)
//Koppla från servern på TCP/IP-port för low Level
}
else
{
// Signalera att kön är tom och vänta på meddelande
m_lowLevelAvail.Reset();
// Blokera tråden tills den väcks genom att AddType2Session,
// AddType3Session eller FileSystemWatcher kör m_lowLevelAvail.Set()
m_lowLevelAvail.WaitOne();
}
}
}
//Lägger en order på en typ 2-transaktion av typen lågnivå
//Körs med klocka. Kan avlutas när som helst
AddType2Session (object state)
{
// Lägg orden på kö
m_lowLevelSessionQueue.Enqueue(Type2);
// meddela lågnivåskö att data finns på kö så att LowLevelSession kör vidare
m_lowLevelAvail.Set();
}
//Lägger en order på en typ 3-transaktion av typen lågnivå
//Körs med klocka. Kan avlutas när som helst
AddType3Session (object state)
{
// Lägg orden på kö
m_lowLevelSessionQueue.Enqueue(Type3);
// meddela lågnivåskö att data finns på kö så att LowLevelSession kör vidare
m_lowLevelAvail.Set();
}
//Återanslut högnivå som ska vara ansluten hela tiden
//Körs med klocka
ReconnectHL()
{
//Anslut till server via TCCP/IP-port för High Level
//Initiera High Level-kommunikation mot servern
SendDataToServer(init)
//Kontrollera att ACK mottages från servern
ReadDataFromServer(Ack)
//Starta timer som regelbundet hämtar data från servern via High Level-kommunikation
m_timerHL = new Timer(new TimerCallback(RunHL), m_rate);
//Om allt lyckades stängs timern som gör uppkoppling av.
//Annars utförs ett nytt uppkopplingsförsök om en stund,
if(lyckades)
m_timerReconnectHL.dispose();
}
//Hämta data av typen högnivå från servern. Körs med klocka
RunHL()
{
//Fråga efter data
SendDataToServer(request)
//Läs data från servern
ReadDataFromServer(Response)
}
//Återanslut dämpkvot som ska vara ansluten hela tiden
//Körs med klocka.
ReconnectDR()
{
//Anslut till server via TCP/IP-port för DR
//Starta tråd som hela tiden väntar på att servern ska skicka data
m_tDecayRatio = new Thread(new ThreadStart(DecayRatio));
//Om allt lyckades stäng stängs timern som gör uppkoppling av.
//Annars utförs ett nytt uppkopplingsförsök om en stund,
if(lyckades)
m_timerReconnectDR.dispose();
}
//Hämta data av typen dämpkvot från servern. Egen tråd
DecayRatio()
{
//Initiera DecayRatio-kommunikation
SendDataToServer(init)
//Kontrollera att ACK mottages från servern
ReadDataFromServer(ack)
//En evighetsloop som hela tiden läser den datan som skickas
while(true)
{
//Läs data från servern
ReadDataFromServer(response)
//Skicka ACK
SendDataToServer(ack)
Thread.Sleep(SleepInterval);
}
}
//Läs data från servern och lägg det i objektet dataToRead
ReadDataFromServer(dataToRead)
{
forEachItemInData(dataToRead)
{
while(NotTimeout)
{
if(NetworkStream.DataAvailable)
{
ReadDataFromStream()
}
Thread.Sleep(SleepInterval);
}
}
}
//Skriv all data i objektet dataToSend till servern
SendDataToServer(dataToSend)
{
forEachItemInData(dataToSend)
{
SendDataToStream()
}
}
Sv: Avsluta alla trådar på ett snyggt sätt
Med flaggor så kan det t.ex. vara en massa booleans (om du kör med polling, vilket inte rekommenderas pga. att om programmet inte gör någonting så tvingar det enda datorn att jobba), eller någon form av events (*ResetEvent-sakerna är trevliga, WaitHandle etc. också. Om du kör med async-sockets så får man en waithandle, om du sedan kombinerar det med *ResetEvent som också har en waithandle så kan du välja att tråden ska frysas till antingen avstängningsflaggan är satt (resetevente), eller att data har kommit (socketen))