webForumDet fria alternativet

Förslag på timeout funktion?

.NET

31 svar · 672 visningar · startad av Nickemannen

Medlem sedan aug. 20003 575 inlägg
Frågan#1

Någon som har något förslag på hur man kan göra
när man försöker göra något men att den grejen inte får ta
för lång tid
så att man kan skriva att

funktionen timed-out ungefär ?

Medlem sedan apr. 20012 266 inlägg
#2

Du tänker på ditt Half-life Server Stat projekt igen? :)

I så fall letar jag efter samma sak, en grej som sätter stop för om det tar för lång tid eller om inte servern hittas.

Medlem sedan aug. 20003 575 inlägg
#3

jepp, men det borde ju att göra en while loop med timer.

men det kanske finns något bättre

Medlem sedan dec. 19991 072 inlägg
#4

Kan du inte lägga anropet på en tråd, sen kan du kolla med ex. en timer (el. annan funktion) om det går för lång tid och i så fall så stoppar du bara tråden?

Medlem sedan aug. 20003 575 inlägg
#5

hittade denna i PHP men det borde gå att göra samma med

.NET

    $starttime=$this-\>timenow();
    do {
        $serverdata.=fgetc($cssocket);
        $serverdatalen++;
        $socketstatus=socket_get_status($cssocket);
        if ($this-\>timenow()\>($starttime+$waittime)) {
            $this-\>errmsg="Connection timed out";
            fclose($cssocket);
            return "";
        }
    } while ($socketstatus\["unread_bytes"\] );
Medlem sedan aug. 20003 575 inlägg
#6

hur menar du fredrik?

har du lite kod så man fattar bättre =

Medlem sedan juni 20022 599 inlägg
#7

En ful och primitiv trådvariant utan felhantering kan kanske se ut så här:

private Boolean bAllDone;
private long lTimeOut=2000; //2 sekunder t.ex ..

private void minTradStart() //Trådfunktion
	{
		// Gör nåt tidskrävande här.
		bAllDone=true;
	}

// Huvudprogram.
bAllDone=false;
Thread t = new Thread(new ThreadStart(minTradStart));
t.Name = "mintrad"
t.Start();
Thread.Sleep(lTimeOut); //Egentligen kan man göra nåt nyttigt här ..
if(bAllDone) //Blev tråden färdig innan time-outen?
{
	//Funktionen timade inte ut.
}
else
{
	//Funktionen timade ut.
}
Medlem sedan aug. 20003 575 inlägg
#8

Tack

Medlem sedan aug. 20003 575 inlägg
#9

hmm, man kan inte skicka info med

Thread tThread = new Thread(new ThreadStart(getserverinfo(strIP,strPort)));

Då frågar den efter method name
det är bara
Thread tThread = new Thread(new ThreadStart(getserverinfo));

som fungerar :(

Medlem sedan aug. 20003 575 inlägg
#10

hmm, jag fick göra strängarna public

Medlem sedan aug. 20003 575 inlägg
#11

nu fungerar allt tack!

Medlem sedan juni 20022 599 inlägg
#12

Ett bra sätt om du vill skicka med parametrar är annars att "kapsla in" trådfunktionen i en klass:

public class ServerInfo {

private string _ip = "";
private int _port = 0;

public ServerInfo(string ip, int port) {
_ip = ip;
_port = port;
}

public void minTradStart() //Trådfunktion
	{
		// Använd _ip och _port här.
		bAllDone=true;
	}
}

//Huvudprogram

ServerInfo s=new ServerInfo(strIP,strPort)
Thread t = new Thread(new ThreadStart(ServerInfo.minTradStart));
t.Name = "mintrad"
t.Start();
Medlem sedan aug. 2001458 inlägg
#13

Att starta trådar är något man bör undvika, en mycket bättre metod är att använda asynkrona anrop.

Jag vet inte riktigt vad du vill göra, så det är svårt att ge ett passande exempel. Men anta att du vill koppla upp en socket, då är rätt metdo socket.BeginConnect() istället för socket.Connect(). BeginConnect() kommer då returnera direkt, sedan får du en callback-funktion när uppkopplingen är klar (eller gick fel).

Medlem sedan aug. 20003 575 inlägg
#14

Det är så att jag använder
UdpClient för att komma åt information från HL-Servrar

och så skickar jag lite kod och skall få ett svar
men om jag inte får det svaret så står allt stilla ett bra tag
tills hela sidan blir error

Vad får man för callback ?

Medlem sedan aug. 2001458 inlägg
#15

Det som i grunden används av UdpClient är en Socket, som har metoder för asynkron använding. UdpClient är dessvärre lite tråkig eftersom den inte stöder asynkron använding.

Lyckligtvis exponerar den socketen som används som en protected property. Jag drog nytta av det och ärvde UdpClient till en klass AdvancedUdpClient, som lägger till BeginReceive/EndReceive. Min ärvda klass bara skickar vidare anropen till Socket:en. Jag har som du ser bara gjort Receive, så resten (Send/Connect) får du klura ut själv :).

Därtill ett litet testprogram, som använder AdvancedUdpClient. All kod är garanterat otestad, så du får väl provköra och se hur det verkar ;).

using System;
using System.IO;
using System.Net.Sockets;
using System.Net;

class UdpTest {
	/// <summary>
	/// Istället för UdpClient för att kunna använda asynkront
	/// </summary>
	AdvancedUdpClient Udp;

	/// <summary>
	/// Buffer där Receive lagrar data
	/// </summary>
	byte[] receiveBuffer = new Byte[10000];

	/// <summary>
	/// Callback som anropas när Receive är klar (lyckad eller misslyckad)
	/// </summary>
	AsyncCallback callbackOnReceive = new AsyncCallback(OnReceive);

	/// <summary>
	/// Denna kommer anropas när vi får något på socket, eller när
	/// något fick fel med att ta emot. I vilket fall ska vi ropa
	/// på EndReceive. Om EndReceive kastar exception gick det dåligt,
	/// annars gick det bra och returvärdet är så många bytes
	/// som finns i bufferten.
	/// </summary>
	/// <param name="ar"></param>
	static void OnReceive(IAsyncResult ar) {
		UdpTest udpTest =  (UdpTest) ar.AsyncState;
		AdvancedUdpClient Udp = udpTest.Udp;
		try {
			int size = Udp.EndReceive(ar);
			// vi har nu tagit emot <size> bytes, och dessa ligger först i
			// udpTest.receiveBuffer
			Console.WriteLine("fick " + size.ToString() + " bytes");
		}
		catch (IOException ex) {
			Console.WriteLine("fel: " + ex);
		}
	}

	void Test() {
		Udp = new AdvancedUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
		Udp.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, callbackOnReceive, Udp);
	}

	static void Main() {
		// kör testprogrammet
		UdpTest udpTest = new UdpTest();
		udpTest.Test();

		// vänta på <enter> så vi inte avslutar direkt
		Console.ReadLine();
	}
}

/// <summary>
/// Utökar UdpClient så den kan användas asynkront. Bara Receive
/// är gjord.
/// </summary>
public sealed class AdvancedUdpClient : UdpClient {
	public AdvancedUdpClient(IPEndPoint ep) : base(ep) {
	}

	public IAsyncResult BeginReceive(byte[] buffer,
							int offset, int size, SocketFlags socketFlags,
							AsyncCallback callback,
							object state) {
		return Client.BeginReceive(buffer, offset, size, socketFlags, callback, state);
	}

	public int EndReceive(IAsyncResult asyncResult) {
		return Client.EndReceive(asyncResult);
	}
}
Medlem sedan aug. 20003 575 inlägg
#16

tack skall titta på det

hinner dock inte nu

Medlem sedan juni 20022 599 inlägg
#17

Fast om jag tolkar Nickemannen rätt så tänker han köra koden i en ASP.NET-sida?

Nickemannen skrev:

tills hela sidan blir error

Då är väl inte asynkrona anrop så lyckade eftersom sidan kan ha terminerat innan callbacken kommer?

Jag hade nog använt trådar och sedan använt Join och inväntat trådarna med en lämpligt vald timeout:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemthreadingthreadclassjointopic3.asp

Annat bra exempel:

http://www.c-sharpcorner.com/Code/2002/Mar/MultiThreadPortScannerTLam.asp

Medlem sedan aug. 2001458 inlägg
#18

niko skrev:

Fast om jag tolkar Nickemannen rätt så tänker han köra koden i en ASP.NET-sida?
...
Då är väl inte asynkrona anrop så lyckade eftersom sidan kan ha terminerat innan callbacken kommer?

Sant. Använd isåfall en asynkron http handler för att klara detta, se http://www.codeguru.com/net_asp/Asynchronous.html

niko skrev:

Jag hade nog använt trådar och sedan använt Join och inväntat trådarna med en lämpligt vald timeout:

Nu vet jag inte om prestanda är något problem här. Det kanske det finns CPU-cykler så det räcker och blir över, och det kanske är ok att skapa trådar. Men eftersom jag sett så många fall där man skapar trådar hej vilt och sedan inte förstår varför applikationen inte presterar bra känner jag mig manad att göra ett inlägg om detta.

Det är dåligt att skapa trådar som inte behövs. Anta att 10 personer tittar på sidan ungefär samtidigt, och att det tar 5 sekunder att få svar på udp-receive. Då skapar du ungefär 50 trådar - det är många trådar i en och samma process. Det är inte ens säkert att du får göra det, man kan begränsa sådant, eftersom många trådar har en enormt negativ påverkan på operativsystemets prestanda. Medan min asynkrona applikation körs på en och samma tråd och gör det som behöver göras, måste operativsystemet in och context-switcha (byta tråd) 50 gånger för din applikation. Det innebär att spara undan CPU-state och switcha user- och kernel-mode stackarna. Lägg på lite last så kommer ditt program inte göra något annat än att byta trådar. Värre är att du kommer minska chansen att få cache-träffar, vilket leder till fler sidfel för att hämta data från RAM. Det är dessutom dyrt att skapa en tråd.

Asynkron kod använder en trådpool genom "I/O completion ports" som stöds av Win32, lite svårare att använda, men mycket bättre. Dålig trådhantering är en vanlig orsak till dålig prestanda, och ofta är det komplicerat att ändra designen i efterhand.

Medlem sedan apr. 20012 266 inlägg
#19

Kan lägga till en liten sak, för att slippa att det görs en Udp request varje gång så kan man cacha datan i t.ex 5 minuter beroende på hur aktuellt värdet måste vara.

Medlem sedan juni 20022 599 inlägg
#20

developer>>

Håller med om att man inte ska skapa trådar i onödan. Fast i detta fallet ser jag även vissa fördelar.

1. Snygg och enkel hantering av timeout-kriteriet. (Vilket egentligen var huvudfrågan här om jag inte minns fel? ;) )


for ( i = 0; i < MAX_THREADS; i ++ ) {
	lThreads[i] = new Thread( new ThreadStart( minTradFunktion) );
	lThreads[i].Start();
}

for ( i = 0; i < lThreads.Length; i ++ )
	if lThreads[i].Join(timeOut)
		//Tråd nr i blev färdig innan time-outen. Gör något ..
	else
		//Tråd nr i blev INTE färdig innan time-outen. Döda tråden ..

2. Koden skalar automatiskt till flera processorer. En singeltrådad version med asynkrona anrop kommer att köa alla callbackanrop på samma processor. (Eller är jag ute och cyklar?)

Och slutligen, som renholm påpekar, om man får prestandaproblem så finns ju alltid output-cachning .. Om tio personer tittar på sidan samtidigt så är det knappast troligt att finns någon anledning att köra koden tio ggr? Så tidskritiskt lär det sällan vara ..

268 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
128 ms — deklarationer (db)
0 ms — hämta statistik (cache)
137 ms — hämta tråd, inlägg och bilagor (db)
128 ms — ändringar (db)