Programm reagiert nicht beim Lesen von großen Dateien mit StreamReader



  • 1. Den hab ich gelesen. Ist mir klar.
    2. Ok
    3. Das ist mir klar. Ich hab dort nur zum Debuggen einen Breakpoint gesetzt, falls was passiert.
    4. Das hab da wohl grade aus meinem letzten Versuch kopiert. Den Writer flushe ich hoffentlich schon über writer->AutoFlush=true;
    5. Ich hab es schon ohne probiert, nur seh ich dann ja nicht mehr wo der StreamReader steht.

    System::Void Form1::ProcessFile(System::String^ filein, String^ fileout)
    {
        //Start reading
        StreamReader^ reader=gcnew StreamReader(diaOpen->FileName);
        StreamWriter^ writer=gcnew StreamWriter(diaSave->FileName);
        writer->AutoFlush=true;
        String^ laststr="";
        String^ s="";
        try
        {
            while(!reader->EndOfStream)
            {
                s=reader->ReadLine();
                if(laststr==s)
                {
                    //Dublette gefunden
                }
                else
                {
                    laststr=s;
                    writer->WriteLine(s);
                }
                Form1::Text=title+" "+((reader->BaseStream->Position/(reader->BaseStream->Length-1))*100).ToString()+"%";
                this->Refresh();
            }
        }
        catch(Exception^ ex)
        {
        }
        reader->Close();
        writer->Close();
    }
    

    Das wär dann die Rohversion.



  • Tjo...

    Ist Form1::Text ein String? Zeichnest du den selbst?

    Dann zeig mal den Code her.

    Am besten alle Funktionen in Form1 die du überschrieben hast (overrides), und alle Event-Handler die du connectest.

    Und natürlich auf jeden Fall den Code der Funktion welche ProcessFile aufruft.

    Ohne weitere Infos sehe ich da schwarz. Bis auf die Vermutung, dass das Problem irgendwie über this->Refresh() getriggert wird kann ich sonst nichts anbieten.



  • DANKE!

    Es war wohl so, hustbaer.
    Ich hab mir noch mal eine kleinere Testdatei erstellt, damit ich sehe, ob das Programm hängt wenn ich das Refresh auskommentiert habe. Da ging es jetzt richtig schnell (1300 [EDIT: nicht 7000. Hab nochmal nachgemessen..] Strings statt 125 pro Sekunde).
    Ich glaube, dass es zu viele Refreshs für die ganze Form pro Zeiteinheit waren.
    Jetzt wäre nur noch interessant, wie ich ein einzelnes Label aktualisieren kann.

    Und das ist wohl ganz einfach mit myLabel->Refresh(); zu erledigen!

    👍 👍 👍

    Ich hätte jetzt aber nicht erwartet, dass Form->Refresh's so tödlich enden können. 😉



  • Ich habe aber leider weiterhin das Problem, dass die Controls sich nicht mehr refreshen, wenn die Form den Fokus verloren hat. Wird wohl irgendwas mit den Validate Funktionen zu tuen haben. Nur weiß ich da nicht weiter..



  • Wenn du das wirklich sauber hinkriegen willst, musst du meiner Meinung nach Threads verwenden.

    Es geht auch ohne, aber das wird schnell zu einem ganz grausamen Gebastel. (Ohne grausames Gebastel bekommst du sonst wieder die selben Probleme wie du schon durch this->Refresh() hattest)

    Der nötige Code mit Threads ist auch hübsch einfach, WENN man sich bereits mit Threads auskennt, weiss worauf man zu achten hat, wie man sie verwendet etc. Dummerweise ist das eine Voraussetzung die es in sich hat 🙂



  • Das lässt sich nur _sinnvoll_ mit Threads lösen. Man kann auch BackgroundWorker verwenden, das macht es etwas einfacher...



  • Jochen Kalmbach schrieb:

    Man kann auch BackgroundWorker verwenden, das macht es etwas einfacher...

    Naja, das macht leider auch nur den einfacheren Teil einfacher, nämlich halt eigenen Code in einem anderen Thread zu laufen zu bekommen.

    Die meisten Fehler machen die Leute meiner Erfahrung nach beim Synchronisieren.



  • Den BackgroundWorker hatte ich mir schon kurz angeschaut, aber wollte nicht so richtig. Mit Threadaufteilung (und vllt auch Multicore-Support?) hab ich mich noch nicht beschäftigt.

    Daher ist es jetzt mal eine Gelegenheit dazu. Ich schau mir aber erstmal selber an, wie es gehen soll. :p



  • So wie ich das verstanden habe, übernimmt er diese ganze Arbeit der Synchronisierung!
    http://msdn.microsoft.com/de-de/library/system.componentmodel.backgroundworker(VS.80).aspx

    Man muss also wirklich nur noch die "Arbeitsmethode" schreiben und sich auf die Events registrieren; diese werden dann im GUI-Thread aufgerufen.



  • Jochen Kalmbach schrieb:

    So wie ich das verstanden habe, übernimmt er diese ganze Arbeit der Synchronisierung!
    http://msdn.microsoft.com/de-de/library/system.componentmodel.backgroundworker(VS.80).aspx

    Aber kann ich in einem Background-Worker for(each auf einen Container machen, der zugleich von der GUI mit Add verändert werden könnte?

    Jochen Kalmbach schrieb:

    Man muss also wirklich nur noch die "Arbeitsmethode" schreiben und sich auf die Events registrieren; diese werden dann im GUI-Thread aufgerufen.

    Ja, in dieser Richtung ist alles OK. Wobei ich mir die Events irgendwie nicht schmecken. Ich glaube, Invoke mit Delegates wäre mehr mein Stil. Vielleicht. Habe da noch zu wenig Input.



  • volkard schrieb:

    Jochen Kalmbach schrieb:

    So wie ich das verstanden habe, übernimmt er diese ganze Arbeit der Synchronisierung!
    http://msdn.microsoft.com/de-de/library/system.componentmodel.backgroundworker(VS.80).aspx

    Aber kann ich in einem Background-Worker for(each auf einen Container machen, der zugleich von der GUI mit Add verändert werden könnte?

    Um sowas musst Du Dich natürlich selber kümmern...

    Aber für "normale" Dinge, wie den Fortschritt berichten, gibt es ja das "ReportProgress"!
    http://msdn.microsoft.com/de-de/library/a3zbdb1t.aspx



  • Könnte ungefähr so aussehen:
    Den Fortschritt aus den gegebenen Informationen zu berechnen ist gar nicht so einfach, wesshalb ich es auch weggelassen habe.

    #pragma once
    
    namespace Test {
    
    	using namespace System;
        using namespace System::IO;
        using namespace System::Diagnostics;
    	using namespace System::ComponentModel;
    	using namespace System::Windows::Forms;
    
    	/// <summary>
    	/// Summary for Form1
    	///
    	/// WARNING: If you change the name of this class, you will need to change the
    	///          'Resource File Name' property for the managed resource compiler tool
    	///          associated with all .resx files this class depends on.  Otherwise,
    	///          the designers will not be able to interact properly with localized
    	///          resources associated with this form.
    	/// </summary>
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    
                backgroundWorker = gcnew BackgroundWorker();
                backgroundWorker->WorkerReportsProgress = true;
                backgroundWorker->DoWork += gcnew DoWorkEventHandler(this, &Form1::OnDoWork);
                backgroundWorker->ProgressChanged += gcnew ProgressChangedEventHandler(this, &Form1::OnProgressChanged);
                backgroundWorker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &Form1::OnRunWorkerCompleted);
    		}
    
    	protected:
    		/// <summary>
    		/// Clean up any resources being used.
    		/// </summary>
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    
                delete backgroundWorker;
    		}
    
        private: BackgroundWorker^ backgroundWorker;
    
        private: System::Windows::Forms::TextBox^  sourcePathTextBox;
        private: System::Windows::Forms::TextBox^  targetPathTextBox;
        private: System::Windows::Forms::Label^  sourcePathLabel;
        private: System::Windows::Forms::Label^  targetPathLabel;
        private: System::Windows::Forms::Button^  processButton;
        private: System::Windows::Forms::Label^  statusLabel;
    
    	private:
    		/// <summary>
    		/// Required designer variable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Required method for Designer support - do not modify
    		/// the contents of this method with the code editor.
    		/// </summary>
    		void InitializeComponent(void)
    		{
                this->sourcePathTextBox = (gcnew System::Windows::Forms::TextBox());
                this->targetPathTextBox = (gcnew System::Windows::Forms::TextBox());
                this->sourcePathLabel = (gcnew System::Windows::Forms::Label());
                this->targetPathLabel = (gcnew System::Windows::Forms::Label());
                this->processButton = (gcnew System::Windows::Forms::Button());
                this->statusLabel = (gcnew System::Windows::Forms::Label());
                this->SuspendLayout();
                // 
                // sourcePathTextBox
                // 
                this->sourcePathTextBox->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Left) 
                    | System::Windows::Forms::AnchorStyles::Right));
                this->sourcePathTextBox->Location = System::Drawing::Point(12, 29);
                this->sourcePathTextBox->Name = L"sourcePathTextBox";
                this->sourcePathTextBox->Size = System::Drawing::Size(337, 20);
                this->sourcePathTextBox->TabIndex = 0;
                this->sourcePathTextBox->Text = L"C:\\input.txt";
                // 
                // targetPathTextBox
                // 
                this->targetPathTextBox->Anchor = static_cast<System::Windows::Forms::AnchorStyles>(((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Left) 
                    | System::Windows::Forms::AnchorStyles::Right));
                this->targetPathTextBox->Location = System::Drawing::Point(12, 68);
                this->targetPathTextBox->Name = L"targetPathTextBox";
                this->targetPathTextBox->Size = System::Drawing::Size(337, 20);
                this->targetPathTextBox->TabIndex = 0;
                this->targetPathTextBox->Text = L"C:\\output.txt";
                // 
                // sourcePathLabel
                // 
                this->sourcePathLabel->AutoSize = true;
                this->sourcePathLabel->Location = System::Drawing::Point(12, 13);
                this->sourcePathLabel->Name = L"sourcePathLabel";
                this->sourcePathLabel->Size = System::Drawing::Size(66, 13);
                this->sourcePathLabel->TabIndex = 1;
                this->sourcePathLabel->Text = L"Source Path";
                // 
                // targetPathLabel
                // 
                this->targetPathLabel->AutoSize = true;
                this->targetPathLabel->Location = System::Drawing::Point(12, 52);
                this->targetPathLabel->Name = L"targetPathLabel";
                this->targetPathLabel->Size = System::Drawing::Size(63, 13);
                this->targetPathLabel->TabIndex = 1;
                this->targetPathLabel->Text = L"Target Path";
                // 
                // processButton
                // 
                this->processButton->Location = System::Drawing::Point(12, 110);
                this->processButton->Name = L"processButton";
                this->processButton->Size = System::Drawing::Size(75, 23);
                this->processButton->TabIndex = 2;
                this->processButton->Text = L"Process";
                this->processButton->UseVisualStyleBackColor = true;
                this->processButton->Click += gcnew System::EventHandler(this, &Form1::processButton_Click);
                // 
                // statusLabel
                // 
                this->statusLabel->AutoSize = true;
                this->statusLabel->Location = System::Drawing::Point(102, 115);
                this->statusLabel->Name = L"statusLabel";
                this->statusLabel->Size = System::Drawing::Size(127, 13);
                this->statusLabel->TabIndex = 3;
                this->statusLabel->Text = L"Ready to start processing";
                // 
                // Form1
                // 
                this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
                this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
                this->ClientSize = System::Drawing::Size(361, 152);
                this->Controls->Add(this->statusLabel);
                this->Controls->Add(this->processButton);
                this->Controls->Add(this->targetPathLabel);
                this->Controls->Add(this->sourcePathLabel);
                this->Controls->Add(this->targetPathTextBox);
                this->Controls->Add(this->sourcePathTextBox);
                this->Name = L"Form1";
                this->Text = L"Form1";
                this->ResumeLayout(false);
                this->PerformLayout();
    
            }
    #pragma endregion
    
            ref struct ProcessInfo
            {
                ProcessInfo(String^ sourcePath_, String^ targetPath_)
                    : sourcePath(sourcePath_)
                    , targetPath(targetPath_)
                { }
    
                String^ sourcePath;
                String^ targetPath;
            };
    
        private: System::Void processButton_Click(System::Object^  sender, System::EventArgs^  e)
                 {
                     processButton->Enabled = false;
                     statusLabel->Text = L"Processing started...";
    
                     ProcessInfo^ processInfo = gcnew ProcessInfo(String::Copy(sourcePathTextBox->Text), 
                                                                  String::Copy(targetPathTextBox->Text));
    
                     backgroundWorker->RunWorkerAsync(processInfo);
                 }
    
                 void OnDoWork(Object^ sender, DoWorkEventArgs^ e)
                 {
                     ProcessInfo^ processInfo = safe_cast<ProcessInfo^>(e->Argument);
    
                     StreamReader^ reader = nullptr;
                     StreamWriter^ writer = nullptr;
                     try
                     {
                         reader = gcnew StreamReader(processInfo->sourcePath);
                         writer = gcnew StreamWriter(processInfo->targetPath);
    
                         while ( ! reader->EndOfStream)
                         {
                             String^ line = reader->ReadLine();
    
                             // do real processing..
                             System::Threading::Thread::Sleep(200);
    
                             writer->WriteLine(line);
    
                             // report progress with backgroundWorker->ReportProgress(..)
                         }
                     }
                     finally
                     {
                         delete reader;
                         delete writer;
                     }
                 }
    
                 void OnProgressChanged(Object^ sender, ProgressChangedEventArgs^ e)
                 {
                     String^ statusText = String::Format(L"Processing {0}% done...", e->ProgressPercentage);
                     statusLabel->Text = statusText;
                     Debug::WriteLine(statusText);
                 }
    
                 void OnRunWorkerCompleted(Object^ sender, RunWorkerCompletedEventArgs^ e)
                 {
                     processButton->Enabled = true;
                     statusLabel->Text = L"Ready to start processing";
                 }
    };
    }
    


  • Jochen Kalmbach schrieb:

    volkard schrieb:

    Jochen Kalmbach schrieb:

    So wie ich das verstanden habe, übernimmt er diese ganze Arbeit der Synchronisierung!
    http://msdn.microsoft.com/de-de/library/system.componentmodel.backgroundworker(VS.80).aspx

    Aber kann ich in einem Background-Worker for(each auf einen Container machen, der zugleich von der GUI mit Add verändert werden könnte?

    Um sowas musst Du Dich natürlich selber kümmern...

    Genau das meinte ich auch.
    Viele glauben "is eh alles einfach", verstehen nicht dass man da noch irgendwas synchronisieren müsste, und tun es dann auch nicht. Und Badaboom.



  • Gibt es im .NET-Framwork threadsichere Container?



  • volkard schrieb:

    Gibt es im .NET-Framwork threadsichere Container?

    Ja.
    http://msdn.microsoft.com/en-us/library/ms668265.aspx

    Arbeitet halt klassisch mit Lock und wird daher vermutlich nicht gerade schnell sein.



  • hustbaer schrieb:

    volkard schrieb:

    Gibt es im .NET-Framwork threadsichere Container?

    Ja.
    http://msdn.microsoft.com/en-us/library/ms668265.aspx

    Supi! *bookmarked*

    hustbaer schrieb:

    Arbeitet halt klassisch mit Lock und wird daher vermutlich nicht gerade schnell sein.

    Völlig egal. Es geht mir um Vorhingenanntes, daß ich dem Backgroundworker aus der GUI was verfummeln kann. Wenn der User 100 Aktionen pro Sekunde schafft, ist er gut, und das schafft der Rechner alle mal.



  • Ja, ich denke auch dass es egal ist.

    Vor allem wenn man irgendwas zur GUI kommunizieren will. Die GUI selbst ist schon so langsam, da kommt es auch die paar hundert Zyklen auch nimmer an.

    Wollte es nur der Vollständigkeit halber erwähnt haben.


Anmelden zum Antworten