webForumDet fria alternativet

Förslag på timeout funktion?

.NET

31 svar · 672 visningar · startad av Nickemannen · sida 2 av 2

Frågan, av Nickemannen

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 ?

Läs frågan i sin helhet →
Medlem sedan aug. 2001458 inlägg
#21

niko skrev:

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?)

Du cyklar ;). Asynkrona anrop kommer hanteras av trådpoolen, som använder I/O completion ports och optimalt 1 tråd per processor. Windows väljer vilken processor det kommer scheduleras på. I/O completion ports är det överlägset bästa sättet att göra högpreseterande och CPU-skalbara applikationer på.

Skapar du själv flera trådar kommer även dessa iofs scheduleras av Windows till att köras på flera processorer, men det kommer innebära onödiga context-switchar, vilket ger sämre prestanda jämfört med I/O completion ports.

Men visst är det som du säger förmodligen inte kritiskt i det här fallet. Personligen tycker jag det är trevligare att alltid använda asynkron I/O, eftersom det ger en trevlig struktur på koden och samtidigt ger bäst prestanda. Renholms förslag att cacha data är alltid bra.

Om någon vill öka sin förståelse kring prestanda, CPU-skalbarhet eller I/O completion ports kan jag tipsa om ett par artiklar. Se http://msdn.microsoft.com/library/en-us/dndllpro/html/msdn_scalabil.asp från 1995, som faktiskt gäller fortfarande. Lyckligtvis lägger .Net-klasserna ett trevligt abstraktionslager över I/O completion ports, som annars är ganska komplext. Titta även på avsnittet "Thou shalt create lots of threads. The more, the merrier" i artikeln: http://msdn.microsoft.com/workshop/server/iis/tencom.asp. I/O completion ports beskrivs mer detaljerat här: http://www.sysinternals.com/ntw2k/info/comport.shtml

När är det bra att själv skapa trådar? Enligt min uppfattning ska man nästan aldrig göra det om man programmerar .Net. Vill man öka prestanda för I/O-begränsade program är asynkron programmering rätt metod. Har man flera av varandra oberoende CPU-begränsade tunga beräkningar som ska utföras kan man överväga att skapa en tråd per processor för att parallellisera dessa. Enklare är nog ändå att använda ThreadPool.QueueUserWorkItem(), vilket ger samma effekt som att skapa trådar, fast man låter .Net sköta trådhanteringen.

Medlem sedan juni 20022 599 inlägg
#22

developer skrev:

Du cyklar

OK. Men jag har åtminstone den goda smaken att själv påpeka när jag eventuellt är ute och cyklar .. :OO

Medlem sedan aug. 20003 575 inlägg
#23

developer

Jag fattat tyvärr inte så mycket av din kod.

Jag har lite frågor på den,

Var kan jag skicka de byte:n jag vill för att sedan kunna få mitt svar.

Var kan jag sätta koden som gör att den går till nästa steg om servern inte svarar efter t.ex. 500 millisekunder?

Medlem sedan aug. 2001458 inlägg
#24

Först vill jag bara säga att dina problem är ganska svåra att lösa på ett bra sätt. Erfarenhet av multitråd-programmering underlättar och det är nog inget man blir lär sig på en dag.

Om du inte förstår mitt exempel föreslår jag att du sätter breakpoints i början av varje funktion och stegar genom i debuggern tills du förstår flödet. Ett annat angrepps-sätt är att följa något av de andra förslagen i tråden, och på så sätt minska komplexiteten.

> Var kan jag skicka de byte:n jag vill för att sedan kunna få mitt svar.

Du vill alltså först skicka lite data, sen vänta på ett svar från servern? Innan du gör BeginReceive se till att anropa Send() och skicka det du vill (alternativt implementera asynkrona Send som jag gjorde med Receive).

> ...om servern inte svarar efter t.ex. 500 millisekunder?

Det naiva sättet är att göra Sleep(5000) direkt efter BeginReceive. Sedan får du ha en bool-variabel "dataHasArrived" som initieras till false.

När (om) du får callbacken kan du sätter du den till dataHasArrived=true. När sedan Sleepen är klar, tittar den på dataHasArrived. Om true, vet man att man inte har en timeout-situation. Om false, måste man hatera timeout:en, förslagsvis genom att göra m_socket.Close(), vilket kommer göra att man får callbacken. När man i callbacken gör EndReceive kommer den kasta ObjectDisposedException, eftersom man anropat Close. Man fångar då ObjectDisposedException och gömmer att det inträffat. Ungefär såhär:

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");
			udpTest.dataHasArrived = true;
		}
		catch (ObjectDisposedException) {
			Console.WriteLine("fick timeout");
		}
		catch (IOException ex) {
			Console.WriteLine("fel: " + ex);
		}
	}

	private volatile bool dataHasArrived;

	void Test() {
		Udp = new AdvancedUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));

		dataHasArrived = false;
		Udp.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, callbackOnReceive, Udp);

		System.Threading.Thread.Sleep(5000);
		if (!dataHasArrived) {
			// timeout - stäng socketen
			Udp.Close();
		}
	}

	static void Main() {
		// kör testprogrammet

		UdpTest udpTest = new UdpTest();
		udpTest.Test();

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

Tyvärr är detta en naiv lösning i och med att man låser huvudtråden i 5 sekunder när man skickat något. Men jag tror det är bra om du börjar med att förstå det här exemplet innan du försöker dig på någon annan lösning.

Medlem sedan aug. 20003 575 inlägg
#25

Thread varianten som han skrev däruppe har jag redan använt och det fungerade fint, men eftersom du skrev att det är en prestanda slösande grej så blev jag lite orolig för mitt program kommer att öppna och stänga en massa threads nu

Medlem sedan aug. 20003 575 inlägg
#26

Den versionen som du skrev nu fattade jag mycket mer av på något sätt Tack så mycket

Medlem sedan aug. 20003 575 inlägg
#27

Jag får att The NameSpace for AdvancedUdp inte finns

Medlem sedan apr. 20012 266 inlägg
#28

Du missade nog denna:

/// <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
#29

Jepp, såg det efter jag frågat hehe

Medlem sedan apr. 20012 266 inlägg
#30

Finns ett annat mycketr lätt sätt att göra en Timeout funktion på, använd Socket i stället för UdpClient.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections;

public class InfoSocket
{

	public static int Main(String[] args) 
	{
		
		byte[] cmd = { 255, 255, 255, 255, 105, 110, 102, 111, 0 };

		System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();

		try
		{
			Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000);
			socket.Connect(new IPEndPoint(IPAddress.Parse(args[0]), Int32.Parse(args[1])));

			socket.Send(cmd);

			IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
			
			bool done = true;

			byte[] byteRecived = new Byte[1024];

			while(done)
			{

				socket.Receive(byteRecived);
				
				socket.Close();

				done = false;
			}
		}
		catch (System.Net.Sockets.SocketException)
		{
			Console.WriteLine("The request timed-out!");
		}
		catch (Exception)
		{
			Console.WriteLine("An error occured! Please try again.");
		}

		return 0;
        
	}
}
Medlem sedan aug. 20003 575 inlägg
#31

Läste faktiskt det på en annan sida också
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

denna raden kände jag igen och det verkar mycket smidigare

Medlem sedan apr. 20012 266 inlägg
#32

Men det är

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000);

som gör Timeout funktionen.

276 ms totalt · 4 externa anrop · v20260731065814-full.2f471f9e
127 ms — deklarationer (db)
0 ms — hämta statistik (cache)
146 ms — hämta tråd, inlägg och bilagor (db)
125 ms — ändringar (db)