Laden...

Klassen architektur

Erstellt von Xqgene vor 19 Jahren Letzter Beitrag vor 19 Jahren 4.495 Views
X
Xqgene Themenstarter:in
2.051 Beiträge seit 2004
vor 19 Jahren
Klassen architektur

Ich habe gerade ein MSDN-WebCast zum Thema Multithreading angeguckt. Unter Anderem gab’s dort so ein Bsp: Eine Wurzel-Berechnung im Bereich von 0 bis 100000000 soll in einem separaten Thread laufen.

Code zur Aufgabe 1:


using System;
using System.Threading;

namespace ConsoleApplication1
{
	class MathClass
	{
		public double dSum;

		public void Calculate()
		{
			for (int i = 0; i < 100000000; i++)
				dSum += Math.Sqrt(i);
		}
	}
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			MathClass mc = new MathClass();
			
			Thread th = new Thread(new ThreadStart(mc.Calculate));
			th.Start();
			th.Join();

			Console.WriteLine("Ergebnis: {0}", mc.dSum);
			Console.ReadLine();
		}
	}
}

So weit, so gut.

Dann wird die Aufgabe erweitert. Berechnungs-Bereich soll variabel sein. Nachfolgend Code der Umsetzung, die mir etwas „merkwürdig“ vorkommt:

Code zu der erweiterten Aufgabe:

 
using System;
using System.Threading;

namespace ConsoleApplication1
{
	class MathClass
	{
		public double dSum;
		private int iBegin;
		private int iEnd;

		public Thread Calculate(int b, int e)
		{
			iBegin = b;
			iEnd = e;

			Thread th = new Thread(new ThreadStart(CalculateBackgrounf));
			
			return th;
		}

		private void CalculateBackgrounf()
		{
			for (int i = iBegin; i < iEnd; i++)
				dSum += Math.Sqrt(i);
		}
	}
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			MathClass mc = new MathClass();
			
			Thread th = mc.Calculate(0, 100000000);
			th.Start();
			th.Join();

			Console.WriteLine("Ergebnis: {0}", mc.dSum);
			Console.ReadLine();
		}
	}
}

Die Calculate-Methode der MathClass-Klasse anstatt eine Berechnung durchzuführen gibt ein Thread-Objekt, das ich starten soll, zurück.

Meine Lösung wäre diese:


using System;
using System.Threading;

namespace ConsoleApplication1
{
	class MathClass
	{
		public double dSum;
		private int iBegin;
		private int iEnd;
		
		public MathClass(int b, int e)
		{
			iBegin = b;
			iEnd = e;
		}

		public void Calculate()
		{
			for (int i = iBegin; i < iEnd; i++)
				dSum += Math.Sqrt(i);
		}
	}

	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			MathClass mc = new MathClass(0, 100000000);
			
			Thread th = new Thread(new ThreadStart(mc.Calculate));
			th.Start();
			th.Join();

			Console.WriteLine("Ergebnis: {0}", mc.dSum);
			Console.ReadLine();
		}
	}
}

Aber vielleicht sehe ich es falsch, und die Lösung der Referenten war besser, objektorientierter, eleganter, „gekapselter“ (tolles Wort ;D), etc.

Wie würdet ihr so eine Aufgabe lösen?

P
939 Beiträge seit 2003
vor 19 Jahren

Hi Xqgene,

ich habe gestern was mit Threading in .Net 2.0 geschrieben. Da habe ich es in dieser Art gemacht:

class MathClass {

   private double sum;

   public void Calculate(int a, int b) {
      for (int i = a; i < b; i++) {
         sum += Math.Sqrt(i);
      }
   }
}

// Main-Class
static void Main() {

   MathClass mathClass = new MathClass();
   int a = 0;
   int b = 100000;
   ThreadStart threadStart = delegate() { mathClass.Calculate(a, b); };
   Thread thread = new Thread(threadStart);
   thread.Start();
}

Ansonsten käme auch ein asynchroner Delegat in Betracht. Ist besonders gut für Nebenläufigkeiten mit Eingabeparametern und Ergebnis, wie es hier der Fall ist, geeignet.

Die Variante wo als Ergebnis ein Thread-Objekt zurückgegeben wird, gefällt mir auch nicht besonders.

Gruss
Pulpapex

4.506 Beiträge seit 2004
vor 19 Jahren

Hallo Xqgene!

Ich würde als einzige Erklärung geben können, dass mit Deiner Lösung es möglich ist, die Methode Calculate() auch ohne Thread aufrufbar wäre.

Die von Microsoft gegebene Lösung ist durch den Rückgabewert an einen Thread gebunden. Da könnte man ja jetzt überspitzt behaupten, dass der Entwickler dieser Klasse stringenter gedacht hat, als bei Dir, aber ob das vertretbar ist, steht auf einem ganz anderen Blatt.

Ich muss Dir aber völlig recht geben, im ersten Bsp. löst Microsoft das in der Art, dass der thread im Main instanziiert und gestartet wird, im 2. Bsp dann aber davon abweicht, was eindeutig zu Verwirrtheit führt.

Was jetzt "besser" ist, kann ich nun wirklich nicht sagen.

Ahh, vielleicht ein deutlicher Unterschied: in der Microsoft Variante wird ohne jeglichen Konstruktor (außer dem zwangsweisen Standardkonstruktor) verwendet, was eine bessere Möglichkeit darstellt, die Klasse noch zu erweitern.
-> Vielleicht kommen noch 20 andere Berechnungsmethoden dazu, die alle ihren eigenen Bereich brauchen (vielleicht auch nicht immer Integer), dann müssten auch immer 20 verschiedene Überladungen von Konstruktoren her...
Aber dafür gibt es zum Glück die Properties 😉

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

4.506 Beiträge seit 2004
vor 19 Jahren

Pulpapex war schneller und wesentlich eleganter in der Lösung!

Ich hab da mal was bei codeproject gesehen und verwendet:


	#region Class ThreadWithParameter
	/// <summary>
	/// This class is needed to start Thread´s with parameter.
	/// </summary>
	public class ThreadWithParameter
	{
		#region Variable Settings

		/// <summary>
		/// Specifies delegate method (used in constructor).
		/// </summary>
		private ThreadProcedure thrExe;

		/// <summary>
		/// Signals if threads should be stopped or not;
		/// </summary>
		public bool stopping = false;

		/// <summary>
		/// Message list queue with FIFO method.
		/// </summary>
		public ArrayList paramList = new ArrayList();

		#endregion

		#region Constructor
		/// <summary>
		/// Constructor of this class. It is more high-performance to initialize a
		/// own object from this class at the beginning of each application instead
		/// of creating again and again new objects fom this class.
		/// </summary>
		/// <param name="proc">Defines the code which will be executed instead of
		/// the delegate</param>
		public ThreadWithParameter(ThreadProcedure proc)
		{
			this.thrExe = proc;
		}
		#endregion

		#region Delegate definition
		/// <summary>
		/// Delegate which is defined nearer with the constructor. This is the 'link'
		/// to the code which should be executed instead of this delegate.
		/// </summary>
		public delegate void ThreadProcedure(object parameter);
		#endregion
	
		#region Method ThreadExec()
		/// <summary>
		/// This method is invoked by a thread. It only stops if you set the signal
		/// flag 'bool stopping' in the instance object of this class.
		/// </summary>
		public void ThreadExec()
		{
			while (stopping == false)
			{
				if (messList.Count > 0)
				{
					lock ("lockidentifier1")
					{
						thrExe((object) paramList[0]);
						messList.RemoveAt(0);
					}
				}
			}
		}
		#endregion
	}
	#endregion

Ich hab das nur in der Art abgeändert, dass das Ganze für eine Message-Verarbeitung anwendbar ist, aber das Grundprinzip bleibt ja 😉

Übrigends anwendbar ist das Ganze dann so:



			// Create Thread for sending to console
            Thread thrSendConsole = new Thread(new ThreadStart(sendConsoleMessage.ThreadExec));
			thrSendConsole.Name = "thrSendConsole";
			thrSendConsole.Priority = ThreadPriority.Normal;
			thrSendConsole.Start();


Viel Spaß damit
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

X
Xqgene Themenstarter:in
2.051 Beiträge seit 2004
vor 19 Jahren

@Pulpapex: wirklich interessanter Einsatz. Leider in der Form nur unter .NET 2.0 einsetzbar. Umgeschrieben zu 1.1 sieht das nicht mehr so ganz elegant:


Class1
{
		// Main-Class
		static void Main() 
		{
			new Class1();
		}

		MathClass mathClass;
		int a = 0;
		int b = 100000000;

		Class1()
		{
			mathClass = new MathClass();
			ThreadStart threadStart = new ThreadStart(ThreadStart);
			Thread thread = new Thread(threadStart);
			thread.Start();
		}

		void ThreadStart()
		{ 
			mathClass.Calculate(a, b); 
		}
}

P
939 Beiträge seit 2003
vor 19 Jahren

Ein asynchroner Methodenaufruf sieht so aus. Die Variante finde ich am besten geeignet, wenn eine Methode mit Parametern und Rückgabewert als Thread ausgeführt werden soll. Die Thread-Klasse ist besser für ständig laufende "Daemons" geeignet.

class MathClass {

   private double sum;

   public void Calculate(int a, int b) {
      for (int i = a; i < b; i++) {
         sum += Math.Sqrt(i);
      }
   }
}

class MainClass {

   static void Main() {
      AsyncCalculate(0, 10000);
   }

   public void AsyncCalculate(int a, int b) {

      MathClass mc = new MathClass();
      CalculateDelegate calculate = new CalculateDelegate(mc.Calculate);
      AsyncCallback callback = new AsyncCallback(CallbackCalculate);

      // Als Benutzerdaten den Delegaten mitgeben. Wird noch benötigt.
      object userData = calculate;

      // Calculate-Methode asynchron aufrufen. Kehrt sofort zurück.
      calculate.BeginInvoke(a, b, callback, userData);
   }

   private void CallbackCalculate(IAsyncResult ar) {

      // Benutzerdaten auslesen.
      CalculateDelegate calculate = (CalculcateDelegate)ar.AsyncState;

      // Hier könnte auch das Ergebnis abgeholt werden, 
      // wenn Calculate nicht void zurückgeben würde.
      calculate.EndInvoke(ar);
   }
}

delegate void CalculateDelegate(int a, int b);

Gruss

4.506 Beiträge seit 2004
vor 19 Jahren

Jepp!

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”