Meilleur auteur de réponses
mettre en oeuvre le backgroundWorker de manière enchainée

Question
-
Mon besoin est de voir le progrès du document pendant qu'il est traité: ça peut prendre plusieurs dizaines de minutes pour un gros document et c'est pourquoi j'ai besoin d'une progress bar.
Ce problème n'est pas bloquant pour l'instant car je n'ai que des documents qui sont traités en quelques secondes.
Mais je vais m'attaquer à des plus gros documents à partir de la visite chez un constructeur automobile Lundi prochain.je voudrais savoir comment passer les éléments de mesure de mon programme dans la progressBar
je suppose que je dois passer le nombre dans le backgroundWorker
J'ai donc mis dans mon code après avoir calculé le pourcentage de progression l'instruction
J'ai suivi l'exemple donné sur msdn et je ne vois pas ce qui change dans mon exemple.je commence à mettre mon code:
erc CWManager::StartDocAnalysis( System::ComponentModel::BackgroundWorker^ backgroundWorker1, long ulNbWord, bool bNewDoc) { CBloc* pBloc; size_t iSize; nParagraph = 0; erc ulTotalNbWords = 0; Word::Paragraphs^ objParagraphs = objDocument->Paragraphs; // il faut swapper les fichiers de backup bool bStatus = CopyFile( (LPCTSTR)sLogFile.c_str(), (LPCTSTR)sSavedLogFile.c_str(), false); int iStatus = remove( sLogFile.c_str()); for each( Word::Paragraph^ objParagraph in objParagraphs) { System::String^ wsText = objParagraph->default->default; // Récupérer les informations de Layout // Word::Paragraph::Format::ParagraphFormat^ Format = objParagraph->Format->ParagraphFormat; Object^ Style = objParagraph->Style; const wchar_t* chars = (const wchar_t*)(Marshal::StringToHGlobalUni( wsText)).ToPointer(); pBloc = new CBloc; pBloc->wsBlocText.append( chars); Marshal::FreeHGlobal(IntPtr((void*)chars)); //pour chaque mot du paragraph on enregistre le Layout dans la list Elements avec le mot correspondant. // CSchedulerGlobal::AnalyseBlocText(CBloc* pBloc) // appeler SchedulerGlobal pour traiter le paragraph. iSize = pBloc->wsBlocText.size(); if( iSize > 2) ulTotalNbWords = objSchedulerGlobal.AnalyseBlocText( pBloc, ulTotalNbWords, bNewDoc); // il faut appeler le backgroundWorker1_ProgressChanged( System::Object^ sender, ProgressChangedEventArgs^ e) // il faut appeler le backgroundWorker1_ProgressChanged( System::Object^ sender, ProgressChangedEventArgs^ e) erc ProgressPercentage = 100 * ulTotalNbWords / ulNbWord; // e->ProgressPercentage = ProgressPercentage; // void MainMenue::backgroundWorker1_ProgressChanged( System::Object^ sender, ProgressChangedEventArgs^ e) // backgroundWorker1->RunWorkerAsync( numberToCompute ); // worker->ReportProgress( percentComplete ); backgroundWorker1->ReportProgress( ProgressPercentage); } CloseDocument(); return( 0); }
Ce qui convient au compilateur mais j'ai régulièrement le message d'erreur le backgroudWordker n'est pas disponible lors de son premier appel:
Informations supplémentaires : OperationCompleted a déjà été appelé pour cette opération. D'autres tentatives d'appel ne seraient pas conformes.
je n'ai qu'un backgroundWorker Je vous met ci dessous le code correspondant
void MainMenue::InitializeProgressBar() { Shown += gcnew EventHandler( this, &MainMenue::Form1_Shown); // To report progress from the background worker we need to set this property backgroundWorker1->WorkerReportsProgress = true; // This event will be raised when we call ReportProgress backgroundWorker1->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &MainMenue::backgroundWorker1_RunWorkerCompleted ); backgroundWorker1->ProgressChanged += gcnew ProgressChangedEventHandler( this, &MainMenue::backgroundWorker1_ProgressChanged); backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &MainMenue::backgroundWorker1_DoWork ); } void MainMenue::Form1_Shown( System::Object^ sender, System::EventArgs^ e) { // Start the background worker progressBar1->Visible = false; } // Back on the 'UI' thread so we can update the progress bar void MainMenue::backgroundWorker1_ProgressChanged( System::Object^ sender, ProgressChangedEventArgs^ e) { // The progress percentage is a property of e progressBar1->Value = e->ProgressPercentage; } System::Void MainMenue::backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { // The progress percentage is a property of e // This event handler is where the actual, // potentially time-consuming work is done. // Get the BackgroundWorker that raised this event. BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender); // Assign the result of the computation // to the Result property of the DoWorkEventArgs // object. This is will be available to the // RunWorkerCompleted eventhandler. e->Result = ComputeAdvance( safe_cast<Int32>(e->Argument), worker, e ); } // This event handler deals with the results of the // background operation. void MainMenue::backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e ) { /* // First, handle the case where an exception was thrown. if ( e->Error != nullptr ) { MessageBox::Show( e->Error->Message ); } else if ( e->Cancelled ) { // Next, handle the case where the user cancelled // the operation. // Note that due to a race condition in // the DoWork event handler, the Cancelled // flag may not have been set, even though // CancelAsync was called. ResultLabel->Text = "Cancelled"; } else { // Finally, handle the case where the operation // succeeded. ResultLabel->Text = e->Result->ToString(); } */ } // This is the method that does the actual work. For this // example, it computes a Fibonacci number and // reports progress as it does its work. long MainMenue::ComputeAdvance( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e ) { // The parameter n must be >= 0 and <= 91. // Fib(n), with n > 91, overflows a long. if ( (n < 0) || (n > 91) ) { throw gcnew ArgumentException( "value must be >= 0 and <= 91","n" ); } long result = 0; // Abort the operation if the user has cancelled. // Note that a call to CancelAsync may have set // CancellationPending to true just after the // last invocation of this method exits, so this // code will not have the opportunity to set the // DoWorkEventArgs.Cancel flag to true. This means // that RunWorkerCompletedEventArgs.Cancelled will // not be set to true in your RunWorkerCompleted // event handler. This is a race condition. if ( worker->CancellationPending ) { e->Cancel = true; } else { if ( n < 2 ) { result = 1; } else { result = ComputeAdvance( n - 1, worker, e ) + ComputeAdvance( n - 2, worker, e ); } // Report progress as a percentage of the total task. int percentComplete = (int)((float)n / (float)numberToCompute * 100); if ( percentComplete > highestPercentageReached ) { highestPercentageReached = percentComplete; worker->ReportProgress( percentComplete ); } } return result; }
et l'include correspondant
public ref class MainMenue : public System::Windows::Forms::Form { public: MainMenue::MainMenue(void); System::ComponentModel::BackgroundWorker^ backgroundWorker1; SpecificationLoader::CWManager^ objWordManager; protected: /// Nettoyage des ressources utilisées. MainMenue::~MainMenue(); private: int numberToCompute; int highestPercentageReached; System::Windows::Forms::MenuStrip^ menuStrip1; System::Windows::Forms::ToolStripMenuItem^ filesToolStripMenuItem; System::Windows::Forms::ToolStripMenuItem^ openAFileToolStripMenuItem; System::Windows::Forms::ToolStripMenuItem^ exitToolStripMenuItem; System::Windows::Forms::ToolStripMenuItem^ helpToolStripMenuItem; System::Windows::Forms::Label^ ResultLabel; System::Windows::Forms::ProgressBar^ progressBar1; System::ComponentModel::Container ^components; void MainMenue::InitializeComponent(void); System::Void MainMenue::openAFileToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e); System::Void MainMenue::exitToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e); System::Void MainMenue::helpToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e); void MainMenue::InitializeProgressBar(); void MainMenue::Form1_Shown( System::Object^ sender, System::EventArgs^ e); void MainMenue::backgroundWorker1_ProgressChanged( System::Object^ sender, ProgressChangedEventArgs^ e); System::Void MainMenue::backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e); void MainMenue::backgroundWorker1_RunWorkerCompleted( Object^ /*sender*/, RunWorkerCompletedEventArgs^ e ); long MainMenue::ComputeAdvance( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e ); };
ainsi que les déclarations d'initialisation
#pragma region Windows Form Designer generated code /// <summary> /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas /// le contenu de cette méthode avec l'éditeur de code. /// </summary> void MainMenue::InitializeComponent(void) { this->menuStrip1 = (gcnew System::Windows::Forms::MenuStrip()); this->filesToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->openAFileToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->exitToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->helpToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->progressBar1 = (gcnew System::Windows::Forms::ProgressBar()); this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker()); this->ResultLabel = (gcnew System::Windows::Forms::Label()); this->menuStrip1->SuspendLayout(); this->SuspendLayout(); // // menuStrip1 // this->menuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^ >(2) {this->filesToolStripMenuItem, this->helpToolStripMenuItem}); this->menuStrip1->Location = System::Drawing::Point(0, 0); this->menuStrip1->Name = L"menuStrip1"; this->menuStrip1->Size = System::Drawing::Size(1018, 24); this->menuStrip1->TabIndex = 0; this->menuStrip1->Text = L"menuStrip1"; // // filesToolStripMenuItem // this->filesToolStripMenuItem->DropDownItems->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^ >(2) {this->openAFileToolStripMenuItem, this->exitToolStripMenuItem}); this->filesToolStripMenuItem->Name = L"filesToolStripMenuItem"; this->filesToolStripMenuItem->Size = System::Drawing::Size(40, 20); this->filesToolStripMenuItem->Text = L"Files"; // // openAFileToolStripMenuItem // this->openAFileToolStripMenuItem->Name = L"openAFileToolStripMenuItem"; this->openAFileToolStripMenuItem->Size = System::Drawing::Size(137, 22); this->openAFileToolStripMenuItem->Text = L"Open a file"; this->openAFileToolStripMenuItem->Click += gcnew System::EventHandler(this, &MainMenue::openAFileToolStripMenuItem_Click); // // exitToolStripMenuItem // this->exitToolStripMenuItem->Name = L"exitToolStripMenuItem"; this->exitToolStripMenuItem->Size = System::Drawing::Size(137, 22); this->exitToolStripMenuItem->Text = L"Exit"; this->exitToolStripMenuItem->Click += gcnew System::EventHandler(this, &MainMenue::exitToolStripMenuItem_Click); // // helpToolStripMenuItem // this->helpToolStripMenuItem->Name = L"helpToolStripMenuItem"; this->helpToolStripMenuItem->Size = System::Drawing::Size(40, 20); this->helpToolStripMenuItem->Text = L"Help"; this->helpToolStripMenuItem->Click += gcnew System::EventHandler(this, &MainMenue::helpToolStripMenuItem_Click); // // progressBar1 // this->progressBar1->Location = System::Drawing::Point(35, 216); this->progressBar1->Name = L"progressBar1"; this->progressBar1->Size = System::Drawing::Size(909, 23); this->progressBar1->TabIndex = 1; // // ResultLabel // this->ResultLabel->AutoSize = true; this->ResultLabel->Location = System::Drawing::Point(32, 445); this->ResultLabel->Name = L"ResultLabel"; this->ResultLabel->Size = System::Drawing::Size(50, 13); this->ResultLabel->TabIndex = 2; this->ResultLabel->Text = L"Message"; // // MainMenue // this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(1018, 479); this->Controls->Add(this->ResultLabel); this->Controls->Add(this->progressBar1); this->Controls->Add(this->menuStrip1); this->MainMenuStrip = this->menuStrip1; this->Name = L"MainMenue"; this->Text = L"MainMenue"; this->menuStrip1->ResumeLayout(false); this->menuStrip1->PerformLayout(); this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion
J'ai remplacé la fonction récursive par une fonction provisoire et je cherche à comprendre par ou passe le backgroundWorker.
J'ai regardé l'objet au débugger et je ne trouve pas de ReportProgress et donc je ne sait pas ce qui se passe.
J'ai mis un point d’arrêt dans mes méthodesSystem::Void MainMenue::backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e)
long MainMenue::ComputeAdvance( int n, BackgroundWorker^ worker, DoWorkEventArgs ^ e )
et je n'y passe pas., et donc je ne sais pas ou ça plante. Mais la méthode StartDocAnalyse est une méthode de haut niveau qui active 100 000 LOC. elle ne me parrait pas être un bon candidat pour le boulot.
il reste à trouver comment enhaîner les appels au backgroundWorker pour qu'il puisse afficher après chaque paragraphe envoyé l'avance mesurée.
Jean Noël Martin
- Modifié JeanNoel53 vendredi 14 décembre 2012 15:50
Réponses
-
Oui c'est vrai.
Bonne chance avec Renault.
Cordialement,- Marqué comme réponse JeanNoel53 mardi 18 décembre 2012 08:50
Toutes les réponses
-
Bonjour de nouveau
Si vous regardez BackgroundWorker::RunWorkerAsync ici
C’est bien écrit :
Démarre l'exécution d'une opération d'arrière-plan.
L’opération qui sera exécutée c’est &MainMenue::backgroundWorker1_DoWork comme vous l’avez indiquée ici :
backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &MainMenue::backgroundWorker1_DoWork );
Donc le problem c’est que vous ne démarrez pas le BackGroundWorker jamais.
Toujours Aurel
-
Bonjour aussi,
J'ai donc modifié la fonction computeAdvance pour faire une simple affectation e->Result = e->Argument;
Le passe une première fois dans backgroundWorker1_DoWork à l'init.
suite à cette modification je sort proprement après le traitement du premier paragraph de backgroundWorker1_DoWork avec e->Result à 1
pour moi la valeur de la progressbar à 1 ne justifie pas un erreur.
mais je sort immédiatement après la primitive avec l'erreur suivante:
Une exception de première chance de type 'System.InvalidOperationException' s'est produite dans System.dll Une exception non gérée du type 'System.InvalidOperationException' s'est produite dans System.dll Informations supplémentaires : BackgroundWorker est actuellement occupé et ne peut pas exécuter plusieurs tâches simultanément.
Jean Noël Martin
-
Vous executez worker->RunWorkerAsync plusieurs fois?
Vous devez l’executer une seule fois.
Dans ComputeAdvance justement demarez StartDocAnalysis avec les paramètres corrects.
Aurel -
je ne vois pas ce que StartDocAnalyse a à voire avec le backgrondWorker.
Ce que je comprend peut être, c'est que je dois avoir une fonction ComputeAdvance que je dois mettre à jour dans ma boucle des paragraphes.
voulez vous me dire ci c'est juste ou faux?
Jean Noël Martin
-
ComputeAdvance c'est la fonction qui fait le calcul du Fibonacci. Je ne crois pas que vous voulez ça.
Je pense que vous voulez suivre le l'avance sur la procession du document Word. Donc dans MainMenue::backgroundWorker1_DoWork justement appelez StartDocAnalysis.
-
-
Parce-que le backgroundworker ouvre un nouvel fil d’exécution. Quel est la raison pour utiliser le backgroundworker autrement?
Normalement la procession qui occupe plus de temps de calcul s’exécute sur un autre fil d’exécution que celui de l’application (qui gestions les formes).
Comme ça, les formes de l’application reste visibles et s’actualise bien, sans blocage, et la procession se passe comme tache de fond. Chaque fois vous appelez dans le tache de fond backgroundWorker1->ReportProgress, un évènement appelée ProgressChanged c’est envoyée vers la tâche principale (qui gestions les formes).
Dans la tâche principale vous ajoutez un EventHandler correspondent au évènement ProgressChanged avec la ligne en bas.
backgroundWorker1->ProgressChanged += gcnew ProgressChangedEventHandler( this, &MainMenue::backgroundWorker1_ProgressChanged);
Ca indique que chaque fois un évènement ProgressChanged apparais, le système doit exécuter MainMenue::backgroundWorker1_ProgressChanged
C’est la méthode qu’on utilise pour suivre l’évolution d’un processus de longue durée.
-
J'ai retraduit votre conseil dans ce thread
J'ai essayé de mettre StartDocAnalyse dans un thread autonome.
voila la syntaxe que j'ai codé:
Thread^ StartDocAnalysis = gcnew Thread( gcnew ThreadStart( 0, objWordManager->StartDocAnalysis( backgroundWorker1, ulDocSize, bNewDoc/*, objApplication, objDocument, objDocuments*/)));
mais je butte sur la syntaxe pour le faire malgré les conseils trouvés ici et làJean Noël Martin
-
Vous n’avez pas besoin de un nouvel Thread.
Thread = fil d’exécution et le backgroundworker fait ça pour vous.
backgroundWorker1_DoWork est exécutée dans un nouvel thread.
Seulement appelez StartDocAnalysis à l’intérieur du backgroundWorker1_DoWork et il sera exécutée dans le nouvel thread.
-
J'ai un problème pratique à résoudre pour faire cela
le backgroundWordker1_DoWork est lancé à l'initialisation.
StartDocAnalyse à besoin des paramètre récupéré par les forms pour s'exécuter.
il faut donc changer la séquence d'initialisation
Je reprendrai cela mardi. Lundi, je suis chez Renault: dites moi merde. ma demonstration marche sous XP
Jean Noël Martin
-
Vous devez des paramètres vers le backgroundworker.
De point de vue ergonomie de votre appli, ça normalement vous devez un boutton « Demarer analyse » qui exécute seulement backgroundWorker1->RunWorkerAsync.
Ca vas exécuter MainMenue::backgroundWorker1_DoWork dans un nouvel Thread.
Le bouton ne sera accessible que après les paramètres de votre document sont bien initialisées.
-
Bonjour
Je suis d'accord avec vous comme je vous l'ai dit je vais faire ça Mardi. il me reste un petite question:
il faudra une primitive dans le lecteur de Word pour mettre à jour la ProgerssBar. Intuitivement je dirais:
backgroundWorker1->progressChanged( ProgressPercentage);
est ce vrai?
Jean Noël Martin
-
Oui c'est vrai.
Bonne chance avec Renault.
Cordialement,- Marqué comme réponse JeanNoel53 mardi 18 décembre 2012 08:50
-
Vous avez bien modifiée et testée le logiciel?
Cordialement,
- Modifié Aurel Bera mardi 18 décembre 2012 10:11
-
-
J'ai donc essayé cet après midi comme prévu.
J'ai donc:
Retiré l'initialisation de DoWork de initialiseProgressBar qui est devenue:
void MainMenue::InitializeProgressBar() { Shown += gcnew EventHandler( this, &MainMenue::Form1_Shown); // To report progress from the background worker we need to set this property backgroundWorker1->WorkerReportsProgress = true; // This event will be raised when we call ReportProgress backgroundWorker1->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler( this, &MainMenue::backgroundWorker1_RunWorkerCompleted ); backgroundWorker1->ProgressChanged += gcnew ProgressChangedEventHandler( this, &MainMenue::backgroundWorker1_ProgressChanged); }
reporté cette initialisation dans openAFileToolStripMenueItem_click
System::Void MainMenue::openAFileToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { OpenAFile^ newWindow = gcnew OpenAFile( this); newWindow->ShowDialog(); progressBar1->Visible = true; bIsDbConnected = SpecificationLoader::objAligner.InitiateDbTransaction( wsDiskOfTheFile + wsPathOfTheFile + wsFileToOpen, bIsDbConnected, bNewDoc, wsTypeDocument, &wsDatabaseName, &wsDatabaseFileName); pChunkSubject = NULL; objWordManager = gcnew SpecificationLoader::CWManager; objWordManager->StartWordApi(); ulDocSize = objWordManager->OpenWordDocument( wsDiskOfTheFile + wsPathOfTheFile + wsFileToOpen); backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &MainMenue::backgroundWorker1_DoWork ); // c'est ici qu'on démarre l'aligner. //Thread^ StartDocAnalysis = gcnew Thread( gcnew ThreadStart( 0, objWordManager->StartDocAnalysis( backgroundWorker1, ulDocSize, bNewDoc/*, objApplication, objDocument, objDocuments*/))); }
et reporté l'appel du traitement principal dans DoWork:
System::Void MainMenue::backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { // The progress percentage is a property of e // This event handler is where the actual, // potentially time-consuming work is done. // Get the BackgroundWorker that raised this event. BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender); // Assign the result of the computation // to the Result property of the DoWorkEventArgs // object. This is will be available to the // RunWorkerCompleted eventhandler. // erc CAligner::AlignDocumentsPairs(bool bMMIMode, std::wstring sSourceFileName, std::wstring sDocType, std::wstring *sDatabaseType, std::wstring *sDatabaseFileName); objWordManager->StartDocAnalysis( backgroundWorker1, ulDocSize, bNewDoc); }
et au débugger je ne passe jamais dans DoWork; Qu'est je fais de travers?
Jean Noël Martin
-
Comme je vois, n'est pas ce que vous faites, ce que vous ne faites pas:
backgroundWorker1->RunWorkerAsync( );
Je ne le vois pas.
C'est ça qui déclenche l’évènement DoWork qui a comme eventHandler (lire Exécute) MainMenue::backgroundWorker1_DoWork.
Aurel
-
Effectivement c'était celà j'ai modifié le gcnew que j'ai remis au même endroit et j'ai mis le RunWordkerAssync à la place. je suis maintenant dans le débug de l'application. J'ai le même status que sur le répertoire release. donc je vais le debugger.
Jean Noël Martin