Autor Beitrag
marian04
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Do 29.11.12 11:53 
Hallo an alle Forenmitgleider,

ich habe zur Zeit ein kleines Problem mit einer Liste und Multithreading, aber lasst es mich kurz ein wenig ausführlicher beschreiben:

Ich habe einen TCP Listener, der auf den Port 3000 hört und dort abwartet, bis eine bestimmte Information reinkommt.
Dieser Listener schreibt die erhaltenen Informationen in ein Objekt und legt dieses in einer Liste ab. (Thread 1)
Dieser Thread läuft natürlich dauerhaft und wartet IMMER auf Informationen die auf diesem Port eintreffen.

Nun möchte ich diese Liste automatisch durchgehen und zu einer bestimmten Zeit einen weiteren Event auslösen. Der Zeitpunkt, zu welchem der Event ausgelöst werden soll, ist in dem Objekt gespeichert.


Nun zu meinem Problem:

Ich durchlaufe diese Liste mit einer Foreach-Schleife und diese führt wohl zu Fehler, ich weiß mir aber leider nicht zu helfen, wieso genauer dieser Fehler auftritt und wie ich diesen umgehen kann.

Anbei der Code:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
        private void readList()
        {
            System.Diagnostics.Debug.WriteLine("reader");

            while(true)
            {
                foreach (var aufnahmen in aufList)
                {
                    System.Diagnostics.Debug.WriteLine(DateTime.UtcNow.ToUniversalTime().ToString("yyyy''MM''dd''HH''mm"));

                    if (DateTime.UtcNow.ToString() == aufnahmen.startTime) // Vergleicht die aktuelle UTC Zeit mit der eingetragenen Zeit
                    {
                        System.Diagnostics.Debug.WriteLine("Aufnahme gefunden");
                    }

                    System.Diagnostics.Debug.WriteLine("Schleife durchlaufen");
                }
            }
        }


Und hier der Fehler:
ausblenden Quelltext
1:
Eine Ausnahme (erste Chance) des Typs "System.InvalidOperationException" ist in mscorlib.dll aufgetreten.					


Beste Grüße,

Marian

Moderiert von user profile iconChristian S.: Code- durch C#-Tags ersetzt
Moderiert von user profile iconTh69: Beitragsformatierung überarbeitet: überflüssige Leerzeilen entfernt
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.11.12 12:01 
Andert sich die aufList List während du per foreach darüber iterierst? Das wäre nicht zulässig. (Obwohl sich das eigentlich in einer anderen Exception äußern sollte)
Wenn sich die einzelne Instanz in der Liste nicht ändert kannst du einfach Threadsicherheit erreichen indem du über eine Kopie der Liste iterierst.

ausblenden C#-Quelltext
1:
foreach (var aufnahmen in new List<WasAuchImmer>(aufList))					
marian04 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Do 29.11.12 12:05 
Hallo Ralf,

ja die Liste würde sich ändern, denn jedes mal, wenn jemand eine Anfrage an den Port sendet wird ein neues Objekt der Liste hinzugefügt.

Würde eine Kopie der Liste nicht dazu führen, dass ich immer mit einer "alten" Liste arbeite und mir aufgrund dessen neu hinzugekommene Objekte entgehen ?


Beste Grüße,

Marian
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.11.12 12:19 
Zitat:
Würde eine Kopie der Liste nicht dazu führen, dass ich immer mit einer "alten" Liste arbeite und mir aufgrund dessen neu hinzugekommene Objekte entgehen ?


Das ist genau der Sinn der Kopie. Einen Snapshot zu haben der sich nicht mehr ändert und damit Threadsafe ist.

Wenn du etwas anderes willst wird es komplizierter. Das wird entweder darauf hinauslaufen Listenzugriffe mit Locks/Mutexen/ReadWriterLocks zu serialisieren oder anstatt eine Liste ein Queue oder ähnliches zu benutzen. Gibt bestimmt auch noch ein paar andere Wege. Welcher der richtige ist hängt davon ab wie sich der Rest Code verhält also wie und wann sich deine Liste verändert. Da müßte man den gesamten Code betrachten um irgendwas vorzuschlagen.
marian04 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Do 29.11.12 12:22 
An der Gesamtübersicht soll es nicht liegen:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AufnahmenVerwaltungWowza
{
    class Program
    {
        static void Main(string[] args)
        {
          
            Server server = new Server();

            
            Console.ReadKey();
        }
    }
    class Server
    {
        private TcpListener tcpListener;
        private Thread listenThread;
        private Thread listChecker;
        public List<Aufnahmen> aufList = new List<Aufnahmen>();  

        public Server()
        {


            this.tcpListener = new TcpListener(IPAddress.Any, 3000);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listChecker = new Thread(new ThreadStart(readList));
            this.listenThread.Start();
            this.listChecker.Start();
        }

        private void ListenForClients()
        {
            this.tcpListener.Start();

            while (true)
            {
                //blockt bis ein Client connected
                TcpClient client = this.tcpListener.AcceptTcpClient();

                //erstellt ein Thread zum händeln
                //mit verbunden Clienten
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }

        private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            byte[] message = new byte[4096];
            int bytesRead;

            while (true)
            {
                bytesRead = 0;

                try
                {
                    
                    bytesRead = clientStream.Read(message, 04096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead == 0)
                {
                    //Der Client hat die Verbindung unterbrochen 
                    break;
                }

                //Nachrricht ist angekommen 
                ASCIIEncoding encoder = new ASCIIEncoding();
                string[] incomingRecording = encoder.GetString(message).Split('#');
                Aufnahmen aufnahmen = new Aufnahmen(Convert.ToString(incomingRecording[0]), Convert.ToString(incomingRecording[1]), Convert.ToString(incomingRecording[2]), Convert.ToString(incomingRecording[3]),Convert.ToString(incomingRecording[4]));
                System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
                aufList.Add(aufnahmen); // fügt eine Aufnahme der Liste aus Aufnahme Objekten hinzu
                
            }

            tcpClient.Close();
        }

        private void readList()
        {

            System.Diagnostics.Debug.WriteLine("reader");
            while(true)
            {
               // aufList.Exists(auf => auf.startTime = DateTime.UtcNow.ToString());

                foreach (var aufnahmen in aufList)
                {
                    System.Diagnostics.Debug.WriteLine(DateTime.UtcNow.ToUniversalTime().ToString("yyyy''MM''dd''HH''mm"));
                    if (DateTime.UtcNow.ToString() == aufnahmen.startTime)
                    {
                        System.Diagnostics.Debug.WriteLine("Aufnahme gefunden");
                        startRecording(aufnahmen);
                    }
                    System.Diagnostics.Debug.WriteLine("test");
                }

            }

        }


        private void startRecording(Aufnahmen recording)
        {
            CookieContainer cJar;
            string UserAgent = @"Mozilla/5.0 (Windows; Windows NT 6.1) AppleWebKit/534.23 (KHTML, like Gecko) Chrome/11.0.686.3 Safari/534.23";
            HttpWebRequest startRequest = (HttpWebRequest)WebRequest.Create("http://svtv002:8086/livestreamrecord?app=live&streamname=" + recording.Channel + "&action=startRecording&user="+recording.User+"&output=/"+recording.User+"_"+recording.Channel+"_"+recording.Titel+"flv");
            startRequest.UserAgent = UserAgent;
            startRequest.KeepAlive = false;
            startRequest.Method = "GET";
            HttpWebResponse response = (HttpWebResponse)startRequest.GetResponse();
            StreamReader sr = new StreamReader(response.GetResponseStream());
            string response1 = sr.ReadToEnd();
        }

        private void stopRecording()
        {
            // http://svtv002:8086/livestreamrecord?app=live&streamname=" + channelList[channeListBox.SelectedIndex] + "&action=startRecording&output=/Aufnahmen/" + channelList[channeListBox.SelectedIndex] + Environment.UserName + ".flv"
        }
    }
    class Aufnahmen
    {
        private string user;
        private string channel;
        public string startTime;
        private string endTime;

        public string User
        {
            get { return user; }
            set { user = value; }
        }

        public string Channel
        {
            get { return channel; }
            set { channel = value; }
        }

        public string StartTime
        {
            get { return startTime; }
            set { startTime = value; }
        }

        public string EndTime
        {
            get { return endTime; }
            set { endTime = value; }
        }

        public string Titel
        {
            get { return titel; }
            set { titel = value; }
        }

        private string titel;

        public Aufnahmen(string user, string channel, string startTime, string endTime, string titel)
        {
            this.user = user;
            this.channel = channel;
            this.startTime = startTime;
            this.endTime = endTime;
            this.titel = titel;
        }


    }
}


Beste Grüße

Moderiert von user profile iconChristian S.: Code- durch C#-Tags ersetzt
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.11.12 12:57 
Kommen da immer nur Aufnahmen hinzu? Oder sollen die auch irgendwann aus der Liste rausfallen?
marian04 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Do 29.11.12 13:11 
Diese Aufnahmen müssen auch wieder gestoppt werden und fallen damit dann aus der Liste raus.
Das wäre dann der Endzeitpunkt der in der class Aufnahmen gespeichert ist.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 29.11.12 13:36 
Dann gehe mal davon aus das die Liste immer relativ kurz ist (keine Tausenden). Dann würde ich weiterhin mit der Kopie der Liste arbeiten. Jeder Schleifendurchlauf dauert ja vorraussichtlich nur Millisekunden. Wenn dann die Anzeige im schlimmstenfall den Zustand von vor ein paar Millisekunden darstellt kann der betrachtende User das eh nicht unterscheiden. Da macht jeder zusätzliche Aufwand das ~genauer~ darzustellen auch keinen Sinn. Die ReadList() Methode solltest du selbst noch etwas ausbremsem. Das die in Endlosschleife die Liste ausgibt die sich vermutlich nicht so schnell ändert wird dir einen Core deines Prozessors unnötigerweise zu annähern 100% auslasten. Entweder da einen Sleep() einbauen oder keinen echten Thread nehmen sondern einen Threaded Timer der nur in einem sinnvollen zeitlichen Abstand mal die Liste ausgibt.
marian04 Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Do 29.11.12 13:41 
Okay.
Danke für die Info.

Die Liste soll so und so nicht ausgeben werden. Das Programm wird ohne Gui und ohne weiteren Einfluss des Users laufen.


Ich werde das einfach mal probieren mit der Liste.

Beste Grüße