Announcement

Collapse
No announcement yet.

C# Serveranwendung Multithreading Beispiel ?!

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • C# Serveranwendung Multithreading Beispiel ?!

    Hallo zusammen!

    Haben wir irgendwo ein Beispiel für eine in C# geschriebene Serverandwendung welche mehrere Connections parallel abarbeiten kann ?

    Danke und liebe Grüße, Nicholas!

  • #2
    Hallo,

    schau mal: TCP Listen port with multiple connections

    mfG Gü
    "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

    Comment


    • #3
      Für was für eine Technik suchst du den Beispiele?
      IIS oder anderer Webserver, COM+(Enterprise Services) oder anderer ApplicationServer, WCF Hosting, selbst gemacht etc.

      Comment


      • #4
        Der Server leitet lediglich aufgenommene SQL Statements an einen Datenbankserver weiter und gibt Rückgabewerte an den Client zurück...
        Welche Technik würdet ihr da empfehlen...

        Comment


        • #5
          Prinzipiell pro Anfrage ein Thread (vorzugsweise aus dem ThreadPool) der die Anfrage entgegen nimmt - verarbeitet und das Ergebnis ausgibt.

          Details kann ich leider keinen nennen da ich mich mit Server in diesem Sinne nicht beschäftige.

          Anmerkung zum ThreadPool: Standardmäßig wird pro Prozessor(-kern) ein Thread erstellt - d.h. bei 2 werden 2 Thread erstellt wenn der Prozess (die Anwendung) gestartet wird. Die Größe des ThreadPools wird nur dann automatisch erhöht wenn nach 0,5 Sekunden noch kein Thread frei ist. Ist davon auszugehen dass (ständig) mehr als 2 Anfragen abgearbeitet werden sollen so kann die Anfangsgröße des Threadpools mit SetMinSize erhöht werden.

          Wenn es ein vollwertiger Application Server sein soll kann zB das Projekt von Rainbird verwendet werden.

          mfG Gü
          "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

          Comment


          • #6
            Application-Server

            Details kann ich leider keinen nennen da ich mich mit Server in diesem Sinne nicht beschäftige.
            Ha - jetzt kann ich mitreden

            Hab ein Demo gebastelt das deinen Anforderungen entspricht. Die Kommentare im Code sollten erklärend sein.

            Dabei geht es um die Simulation einer 3-Tier-Anwendung.

            Die Logikschicht (Klassenbibliothek):
            [highlight=c#]
            using System;
            using System.Collections.Generic;
            using System.Threading;

            namespace gfoidl.Test.Remoting._3Tier.BusinessComponents
            {
            /// <summary>
            /// Stellt die Logikschicht dar.
            /// </summary>
            /// <remarks>
            /// Muss serialisierbar sein.
            /// </remarks>
            [Serializable]
            public class PersonManager : MarshalByRefObject
            {
            /// <summary>
            /// Simuliert Datenbank.
            /// </summary>
            private List<Person> _personList = new List<Person>
            {
            new Person{Name = "Gü", Alter = 26},
            new Person{Name = "Max", Alter = 26}
            };
            //---------------------------------------------------------------------
            /// <summary>
            /// Simuliert SELECT.
            /// </summary>
            public IEnumerable<Person> GetPersons()
            {
            WriteThreadID();
            Console.WriteLine("GetPersons.");

            return _personList;
            }
            //---------------------------------------------------------------------
            /// <summary>
            /// Simuliert INSERT.
            /// </summary>
            public void AddPerson(Person p)
            {
            WriteThreadID();
            Console.WriteLine("AddPerson: {0}, {1}", p.Name, p.Alter);

            _personList.Add(p);
            }
            //---------------------------------------------------------------------
            /// <summary>
            /// Simuliert UPDATE.
            /// </summary>
            public void UpdatePerson(Person original, int newAge)
            {
            WriteThreadID();
            Console.WriteLine("UpdatePerson: new Age = {0}", newAge);

            Person person = _personList.Find(p => p.Name == original.Name);
            person.Alter = newAge;
            }
            //---------------------------------------------------------------------
            /// <summary>
            /// Simuliert DELETE.
            /// </summary>
            public void DeletePerson(Person p)
            {
            WriteThreadID();
            Console.WriteLine("DeletePerson: {0}, {1}", p.Name, p.Alter);

            _personList.Remove(p);
            }
            //---------------------------------------------------------------------
            /// <summary>
            /// Gibt die akutelle Thread-ID aus. Die Ausgabe erfolgt im
            /// Konsolenfenster des Servers da in diesem Prozess diese Klasse
            /// gehostet wird (wird dort registriert).
            /// </summary>
            private void WriteThreadID()
            {
            // Intern wird ThreadPool verwendet. Standardmäßig wird pro
            // Prozessor(-kern) ein Thread erstellt. Ist der Pool aufgebraucht
            // wird nach 0,5 Sekunden ein neuer Thread erstellt wenn dann
            // noch keiner frei ist. Deshalb wird hier 0,6 Sekunden simuliert
            // um die Erstellung von neuen Threads eher zu erzwingen.
            Thread.Sleep(600);
            Console.Write(
            "Thread-ID: {0}\t",
            Thread.CurrentThread.ManagedThreadId);
            }
            }
            //-------------------------------------------------------------------------
            /// <summary>
            /// Simuliert Datenbank-Tabelle.
            /// </summary>
            /// <remarks>
            /// Muss serialisierbar sein.
            /// </remarks>
            [Serializable]
            public class Person
            {
            public string Name { get; set; }
            public int Alter { get; set; }
            }
            }
            [/highlight]

            Der Server (Konsolenanwendung, Verweis auf die Logikschicht):
            [highlight=c#]
            using System;
            using System.Runtime.Remoting;
            using System.Runtime.Remoting.Channels;
            using System.Runtime.Remoting.Channels.Tcp;
            using gfoidl.Test.Remoting._3Tier.BusinessComponents;

            namespace gfoidl.Test.Remoting._3Tier.ServerHost
            {
            class Server
            {
            static void Main(string[] args)
            {
            // Server TCP-Kanal auf Port 8085 öffnen und registrieren:
            TcpServerChannel channel = new TcpServerChannel(8085);
            ChannelServices.RegisterChannel(channel, false);

            // Name des .net Remoting Hosts:
            RemotingConfiguration.ApplicationName = "gfoidlDemo";

            // PersonManager für serverseitige Singlecall-Aktivierung
            // konfigurieren.
            // Singlecall: jede Anfrage wird mit einem neuen Objekt durchgeführt.
            // Singletoncall: jede Anfrage wird mit dem gleichen Objekt durchgeführt.
            // Somit ist bein Singeltoncall auf threadsichere Implementierung zu
            // achten.
            // Bei mehreren Anfragen werden diese automatisch auf mehrere Threads
            // aufgeteilt.
            RemotingConfiguration.RegisterWellKnownServiceType (
            typeof(PersonManager),
            "PersonManager",
            WellKnownObjectMode.SingleCall);

            // Hinweis auf Konsole ausgeben:
            Console.WriteLine("Server gestartet...");
            Console.WriteLine("Taste drücken um zu beenden.");
            Console.ReadKey();
            }
            }
            }
            [/highlight]

            Und zu letz noch der Client (Konsolenanwendung, Verweis auf die Logikschicht):
            [highlight=c#]
            using System;
            using System.Collections.Generic;
            using System.Diagnostics;
            using System.Linq;
            using System.Runtime.Remoting;
            using System.Runtime.Remoting.Channels;
            using System.Runtime.Remoting.Channels.Tcp;
            using System.Threading;
            using gfoidl.Test.Remoting._3Tier.BusinessComponents;

            namespace gfoidl.Test.Remoting._3Tier.Client
            {
            class Client
            {
            static void Main(string[] args)
            {
            // Für Testzwecke den Server starten:
            Process server = Process.Start("ServerHost.exe");

            // TcpChannel registrieren. Somit können Instanzen
            // der registrierten Klasse per new erstellt werden.
            // Alternativ können die Instanzen mit Activator.GetObject()
            // erstellt (dabei ist keine Registrierung des Channels
            // nötig).
            // Vorteil: Debugging möglich!
            ChannelServices.RegisterChannel(new TcpChannel(), false);
            RemotingConfiguration.RegisterWellKnownClientType(
            typeof(PersonManager),
            "tcp://localhost:8085/gfoidlDemo/PersonManager");

            // Simulation durchführen. Dabei 3 gleichzeitige Anfragen
            // simulieren.
            // Instanz erstellen. Hier ist der Vorteil der Registrierung
            // des Channels ersichtlich. Der Code zur Instanziierung
            // ist nicht anders als üblich.
            // Für jede Anfrage wird wie im Realfall eine Instanz der
            // BusinessComponente erstellt.
            PersonManager[] personManager = new PersonManager[3];
            for (int i = 0; i < personManager.Length; i++)
            personManager[i] = new PersonManager();
            new Simulation().Run(personManager);

            Console.WriteLine("Ende. Beliebige Taste drücken...");
            Console.ReadKey();
            if (!server.HasExited)
            server.Kill();
            }
            }
            //-------------------------------------------------------------------------
            public class Simulation
            {
            /// <summary>
            /// Führt die Simulation durch. Dabei wird für jeden PersonManager
            /// ein Thread erstellt. Die Threads werden synchron gefeuert.
            /// Es wird bewusst kein ThreadPool eingesetzt.
            /// </summary>
            public void Run(PersonManager[] personManager)
            {
            ManualResetEvent signal = new ManualResetEvent(false);
            Thread[] threads = new Thread[personManager.Length];
            for (int i = 0; i < personManager.Length; i++)
            {
            threads[i] = new Thread((o) =>
            {
            int idx = (int)o;

            // Warten auf den Start:
            signal.WaitOne();

            IEnumerable<Person> persons = personManager[idx].GetPersons();
            personManager[idx].AddPerson(
            new Person { Name = "Andrea", Alter = 27 });
            personManager[idx].UpdatePerson(persons.First(), 27);
            personManager[idx].DeletePerson(persons.First());
            });

            // Thread starten:
            threads[i].Start(i);
            }

            // Threads "loslassen":
            Thread.Sleep(250); // damit die Threads sicher gestartet sind.
            signal.Set();
            }
            }
            }
            [/highlight]

            Aus der Testausgabe ist gut erkennbar dass die Anfragen parellel in verschiedenen Threads abgearbeitet werden:
            Code:
            Server gestartet...
            Taste drücken um zu beenden.
            Thread-ID: 4    GetPersons.
            Thread-ID: 4    GetPersons.
            Thread-ID: 6    GetPersons.
            Thread-ID: 4    AddPerson: Andrea, 27
            Thread-ID: 7    AddPerson: Andrea, 27
            Thread-ID: 6    AddPerson: Andrea, 27
            Thread-ID: 4    UpdatePerson: new Age = 27
            Thread-ID: 7    UpdatePerson: new Age = 27
            Thread-ID: 6    UpdatePerson: new Age = 27
            Thread-ID: 4    DeletePerson: Gü, 26
            Thread-ID: 7    DeletePerson: Gü, 26
            Thread-ID: 6    DeletePerson: Gü, 26
            Happy coding

            mfG Gü
            "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

            Comment


            • #7
              Thank you very much ;-)

              Comment


              • #8
                Der Server leitet lediglich aufgenommene SQL Statements an einen Datenbankserver weiter und gibt Rückgabewerte an den Client zurück...
                Ähm ... wenn der nichts macht ausser SQL und deren Ergebnis durchreichen ist der ziemlich überflüssig. Irgendwas sinnvolles soll der doch bestimmt tun oder?

                Comment


                • #9
                  In einer standart Software ist der sicherlich Überflüssig. Ja...
                  Denn da wird ja das meiste Management von dem DBMS ansich schon erledigt.

                  Was aber wenn du Lizenzsoftware programmierst ?!
                  Dann ist es schon sinnvoll und auch üblich einen Applicationserver laufen zu lassen.

                  Comment


                  • #10
                    Was aber wenn du Lizenzsoftware programmierst ?!
                    Dann ist es schon sinnvoll und auch üblich einen Applicationserver laufen zu lassen.
                    Also doch mehr als SQL durchreichen

                    Comment


                    • #11
                      In meiner Application brauche ich den Server nicht.
                      Ich habe doch lediglich nach einem Beispiel gefragt ;-)

                      Comment


                      • #12
                        In meiner Application brauche ich den Server nicht.
                        Ich habe doch lediglich nach einem Beispiel gefragt ;-)
                        Ma, der ganze Aufwand umsonst

                        mfG Gü
                        "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                        Comment


                        • #13
                          Keineswegs...
                          Nur weil ich es für meine Applikation nicht benötige heisst das nicht dass ich mich nicht für den Code interessiere :-P

                          Comment


                          • #14
                            Hab mich jetzt ein wenig die Thematik "verteiltes Rechnen mit .net" eingearbeitet und würde mit dem aktuellen Wissen sagen dass WCF die wohl beste Variante ist.

                            Der Großteil der Aufgaben die ein Server erfüllen muss (Sitzungs-, Authentifzierungs-, Autorisierungs-, ...-Management) sind ja mehr oder weniger immer die gleichen und diese sind WCF per Konfiguration weitestgehend einstellbar ohne dass selbst viel Code (wie es zB bei Remoting nötig wäre) geschrieben werden muss.

                            Ein weiter Vorteil der mir auffällt ist dass per Konfiguration mehrer Endpunkte mit unterschiedlichen Bindungsarten definiert werden können. Somit kann für jeden Client das passende Verfahren angeboten werden.

                            Was auch noch positiv auffällt ist dass es mit Callbacks vom Server zum Client viel weniger Probleme gibt als mit .net Remoting.

                            Außerdem ist es die von Mircosoft vorgeschlagenen Variante denn .net Remoting wird nicht mehr allzu sehr weiterentwickelt.

                            Zusammenfassend kann gesagt werden dass WCF wohl die Zukunft von verteiltem Rechnen mit .net sein wird.


                            mfG Gü
                            "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                            Comment

                            Working...
                            X