Developpez.com

Télécharger gratuitement le magazine des développeurs, le bimestriel des développeurs avec une sélection des meilleurs tutoriels

FAQ C++/CLI et VC++.NetConsultez toutes les FAQ

Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013 

 
OuvrirSommaireIntéraction du C++/CLI avec le framework .NetThread, Processus

Pour créer un thread, il faut utiliser la classe System::Threading::Thread.
Considérons que l'on dispose d'une Form.
Nous avons besoin de déclarer notre objet thread à l'intérieur de la Form.

 
Sélectionnez

using namespace System::Threading;				
public ref class Threads : public System::Windows::Forms::Form
{
    // ........
    private : Thread ^_threadCalculs1;
}
				

La fonction exécutée par le thread a une signature imposée par le .NET Framework. Il s'agit du délégué System::Threading::ThreadStart.
C'est une fonction qui ne prend aucun paramètre et ne possède pas de valeur de retour.
On peut la déclarer ainsi dans notre Form.

 
Sélectionnez

private :void ThrFunc1()
{ 
    // Traitement effectué par le thread. Calculs est une fonction quelconque de notre Form
    try
    {
        Calculs(1000) ;
    }
    catch (Exception ^ex)
	{
	   System::Diagnostics::Debug::WriteLine(ex->ToString());
    }
}
				

Pour démarrer le thread, on utilise la fonction Start de la classe Thread.

 
Sélectionnez

private : void StartThread()
{
    // ThrFunc1 est la fonction exécutée par le thread.
	_threadCalculs1 = gcnew Thread(gcnew ThreadStart(this, &Threads::ThrFunc1));
    // Il est parfois pratique de nommer les threads surtout si on en créé plusieurs.
    _threadCalculs1->Name = "Thread1"; 
    // Démarrage du thread.
    _threadCalculs1->Start();
}
				
Créé le 9 mai 2006  par nico-pyright(c), abelman

Le délégué System::Threading::ThreadStart utilisé pour les fonctions de thread ne prend pas de paramètres.
Pour passer des paramètres à un thread, il vous faut créer une classe pour contenir les paramètres et la méthode du thread.

 
Sélectionnez

using namespace System;
using namespace System::Threading;	
 
ref class ThreadParametre
{
private:
	String ^_text;
    int _entier;
public:
    // Constructeur
    ThreadParametre(String ^texte, int entier)
    {
        _text = texte;
        _entier = entier;
    }
    // Exécution de la méthode du thread
    void ExecuteThread()
    {
        for(int i = 0; i < _entier; i++)
        {
			Console::WriteLine("Index : " + i);
			Console::WriteLine("Message : " + _text);
        }
    }
};
 
int main(array<System::String ^> ^args)
{
	ThreadParametre ^ExempleThread = gcnew ThreadParametre("Message de test", 5);
	Thread ^t = gcnew Thread(gcnew ThreadStart(ExempleThread, &ThreadParametre::ExecuteThread));
	t->Start();
 
	return 0;
}
				
Créé le 9 mai 2006  par nico-pyright(c), abelman, Thomas Lebrun

Le meilleur moyen de d'arrêter un thread est de laisser sa fonction se terminer.
Si une fonction de thread s'exécute en continu dans une boucle, il est nécessaire d'écrire un code qui prévoit une condition pour sortir de la boucle. Cette condition doit pouvoir être modifiée par d'autres threads.

Reprenons l'exemple de notre Form (voir Q/R création d'un thread).
Pour signaler au thread que nous souhaitons qu'il s'arrête, nous allons utiliser un objet de la classe System::Threading::AutoResetEvent.
Dans la boucle de la fonction du thread, nous faisons attendre le thread pendant un court laps de temps. Si l'objet AutoResetEvent passe à l'état signalé, alors on sort du thread.

 
Sélectionnez

using namespace System;
using namespace System::Threading;	
 
public ref class Threads : public System::Windows::Forms::Form
{
	private : Thread ^ _threadCalculs1;
	// Evènement de signal de fin de thread
	private : AutoResetEvent ^_endThreadCalculsEvent;
public:
	Threads(void)
	{
		InitializeComponent();
		_endThreadCalculsEvent = gcnew AutoResetEvent(false);
	}
 
	private :void ThrFunc1()
	{ 
		// Traitement effectué par le thread. Calculs est une fonction quelconque de notre Form
		try
		{
			Calculs(1000) ;
		}
		catch (Exception ^ex)
		{
			System::Diagnostics::Debug::WriteLine(ex->ToString());
		}
	}
 
	private : void StartThread()
	{
		// ThrFunc est la fonction exécutée par le thread.
		_threadCalculs1 = gcnew Thread(gcnew ThreadStart(this, &Threads::ThrFunc1));
		// Il est parfois pratique de nommer les threads surtout si on en créé plusieurs.
		_threadCalculs1->Name = "Thread1"; 
		// Démarrage du thread.
		_threadCalculs1->Start();
	}
	void Calculs(int tempo)
	{
		// Si l'événement est à l'état signalé, WaitOne renvoie true et la boucle se termine.
		while (! _endThreadCalculsEvent->WaitOne(tempo, false) )
		{
			// C'est ici ou notre thread fait son travail
			// .....
		}
	}
 
	// pour arreter le thread
	private: System::Void button1_Click_1(System::Object^  sender, System::EventArgs^  e) 
	{
		// L'événement passe à l'état signalé
		_endThreadCalculsEvent->Set();
		// On attend la fin du thread.
		_threadCalculs1->Join(); 
	}
};
				

Il existe un moyen plus radical d'arrêter un thread, c'est l'utilisation de la fonction Thread::Abort.
Lorsque vous appelez Abort, le Runtime lève une exception ThreadAbortException que le thread peut alors intercepter.
C'est aussi pourquoi il est déconseillé d'utiliser Abort car on ne peut prévoir où en est le thread dans son traitement. Lever une exception peut interrompre le thread alors qu'il est dans une partie du code qu'il doit terminer avant de sortir.
Un des exemples où on peut utiliser Abort sans risque : la fonction du thread est bloquée infiniment sur un appel (une attente de connexion socket par exemple).

 
Sélectionnez

// Forcer la fin du thread
void AbortThread()
{
    _threadCalculs1->Abort();  // On demande au runtime d'arrêter le Thread
    _threadCalculs1->Join();   //  On attend la fin du thread.
}				
				

Un thread terminé ne peut plus être relancé. Il faut instancier un nouvel objet Thread pour chaque démarrage de thread.

Créé le 9 mai 2006  par nico-pyright(c), abelman

Tout d'abord pensez à la clause:

 
Sélectionnez

using namespace System::Threading;
				

Pour changer le nom du thread courant, ajoutez la ligne de code suivante :

 
Sélectionnez

Thread::CurrentThread->Name = "MainThread";
				

Ici on donne le nom "MainThread" au thread courant.
Si vous devez réaliser cela pour le thread principal, alors il faut ajouter cette ligne avant le "Application::Run(...)".

Créé le 9 mai 2006  par sam_XIII

Il arrive souvent de souhaiter interdire à une application d'avoir plusieurs instances lancées.
Voici une petite classe qui lors du démarrage de l'application, s'assure qu'elle n'est pas déjà en cours d'exécution.
Elle utilise un objet mutex nommé, donc potentiellement visible par tous les autres processus.

 
Sélectionnez

ref class SingleInstanceApp
{
private:
	System::Threading::Mutex ^_siMutex;
	bool _siMutexOwned;
public:
	SingleInstanceApp(String ^name)
	{
		_siMutex = gcnew System::Threading::Mutex(false, name);
		_siMutexOwned = false;
	}
 
	~SingleInstanceApp()
	{
		// Libération du mutex si il a été acquis
		if (_siMutexOwned) 
			_siMutex->ReleaseMutex();
	}
 
	// Application déjà lancée ?
	bool IsRunning()
	{
		// Acquisition du mutex.
		// Si _siMutexOwned vaut true, l'application acquiert le mutex car il est "libre"
		// Sinon le mutex a déjà été acquis lors du lancement d'une instance précédente
		_siMutexOwned =  _siMutex->WaitOne(0, true);
		return !(_siMutexOwned);
	}
};
				

Pour utiliser notre classe, il suffit de procéder ainsi dans le Main de notre application.

 
Sélectionnez

SingleInstanceApp ^app = gcnew SingleInstanceApp("{123456789 - ABCD - EFEG - XXXX}");
if (app->IsRunning())
	MessageBox::Show("Application déjà lancée");
else
	Application::Run(gcnew Form1());
delete app;
				

Important :
Si une application lambda en cours d'exécution crée un mutex ayant le même nom que celui de notre application, cette dernière ne pourra plus se lancer.
Elle se comportera comme si une autre instance de l'application était déjà en cours.
Il existe une technique pour l'éviter mais cela sort de notre sujet. Veuillez donc à choisir un nom assez compliqué pour votre mutex.

Remarque : On appelle explicitement le destructeur avec delete car on ne peut pas libérer le mutex dans le finalizer.

Créé le 9 mai 2006  par nico-pyright(c), abelman

Pour lancer un processus depuis notre application, on utilise la classe System::Diagnostics::Process.
Exemple : lancer une instance de internet explorer qui ouvre www.developpez.com

 
Sélectionnez

void StartProcess()
{
	// Instance de la classe Process
	System::Diagnostics::Process ^proc = gcnew System::Diagnostics::Process();
	// Nom de l'exécutable à lancer
	proc->StartInfo->FileName = "iexplore.exe";
	// Arguments à passer à l'exécutable à lancer
	proc->StartInfo->Arguments="http://www.developpez.com";
	// Démarrage du processus
	proc->Start() ;
	// On libère les ressources dont on n'a plus besoin.
	proc->Close(); // Attention Close ne met pas fin au processus.
}				
Créé le 9 mai 2006  par nico-pyright(c), abelman, freegreg

On peut ouvrir des documents dont l'extension est connue du shell windows comme les .txt ou les .doc avec la classe System::Diagnostics::Process.
Exemple : Ouverture d'un fichier texte .txt.

 
Sélectionnez

//Instance de la classe System.Diagnostics.Process
System::Diagnostics::Process ^ proc = gcnew System::Diagnostics::Process();
//Nom du fichier dont l'extension est connue du shell à ouvrir
proc->StartInfo->FileName = "monfichier.txt";
//Démarrage du processus. Notepad (si il est associé aux fichiers .txt) sera alors lancé et ouvrira le fichier monfichier.txt
proc->Start();
//On libère les ressources
proc->Close();
				
Créé le 9 mai 2006  par nico-pyright(c), abelman

Il est possible de rediriger la sortie standard d'un processus et de l'afficher dans un TextBox multiligne par exemple.

 
Sélectionnez

String ^ RedirectStdOutput(String ^nomProcess)
{
	System::Diagnostics::Process ^ proc = gcnew System::Diagnostics::Process();
	// On désactive le shell
	proc->StartInfo->UseShellExecute = false;
	// On redirige la sortie standard
	proc->StartInfo->RedirectStandardOutput = true;
	// On définit la commande 
	proc->StartInfo->FileName = nomProcess;
	// Démarrage de la commande
	proc->Start();
	// Attente de la fin de la commande
	proc->WaitForExit();
	// Lecture de la sortie de la commande
	String ^ output = proc->StandardOutput->ReadToEnd();
	// Libération des ressources
	proc->Close();
	return output;
}
				
Créé le 9 mai 2006  par nico-pyright(c), abelman

Pour lister les processus en cours on utilise la fonction :

 
Sélectionnez

// Pour tous les processus en cours sur l'ordinateur local
array<System::Diagnostics::Process ^> ^ prc  = Process::GetProcesses();
// Pour tous les processus notepad en cours sur l'ordinateur local
array<System::Diagnostics::Process ^> ^ prc = Process::GetProcessesByName("notepad");
				
Créé le 9 mai 2006  par nico-pyright(c), abelman

Pour arrêter un processus, il faut disposer d'un objet System::Diagnostics::Process qui représente le processus.

 
Sélectionnez

// Pour les applications consoles
proc->Kill();
// Libération des ressources
proc->Close();
				

Pour les applications WinForm il est préférable d'utiliser CloseMainWindow afin que l'application reçoive le message de fermeture et se ferme correctement.

 
Sélectionnez

// Pour les applications avec une interface graphique (et donc une pompe de messages)
// Si l'appel échoue, on peut alors forcer la fermeture avec Kill.
proc->CloseMainWindow();
// Libération des ressources
proc->Close();
				
Créé le 9 mai 2006  par nico-pyright(c), abelman
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006-2007 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.