Vous êtes sur la page 1sur 23

Programmation concurrente

CSI2520, Hiver 2007


Programmation concurrente
• La programmation est distribuée lorsque les
processus ne partagent pas la mémoire
• Sinon la programmation est dite parallèle

CSI2520, Hiver 2007


Processus
• Un processus ou tâche est une instance d'un programme ou d' une
partie d' un programme pouvant être exécuté de façon autonome
• Un langage de programmation concurrente doit permettre:
– la création de processus: des processus concurrents peuvent être créés à
partir de n' importe quel langage en utilisant de mécanismes explicites de
programmation concurrente
– la synchronisation de leurs opérations:
• Synchronisation coopérative: lorsqu' un processus attend la fin de l’exécution
d' un autre avant de poursuivre son exécution.
• Synchronisation compétitive: lorsque plusieurs processus utilise la même
ressource. Il faut alors disposer d' un mécanisme d’exclusion mutuelle afin
d’éviter que les processus interfèrent entre eux
– la communication des données entre processus: en utilisant des
mécanismes de communication inter-processus définis par le système
d’exploitation

CSI2520, Hiver 2007


Fil d’exécution
• Un fil d’exécution (thread) est une séquence d’exécution pouvant ou
non interagir avec d’autres fils
• Les fils partagent souvent les même variables
• Les fils ont souvent (mais pas nécessairement) une durée de vie
limitée
• Un fil peut être bloqué:
– Si il désire utiliser des variables partagées avec d’autres fils
– Si il doit attendre des résultats d’un autre fil
• Une application se subdivise en processus et un processus peut être
composé de fils
• Les processus sont généralement créés lors de la conception de
l’architecture alors que les fils sont créés lors de la phase de
programmation

CSI2520, Hiver 2007


Fils en C++
• En utilisant
– AfxBeginThread(fonction,parametre);

• Le paramètre est un pointeur à des void


• La fonction transmise a comme signature
– UINT fonction(LPVOID connectionP);

CSI2520, Hiver 2007


Exemple
class CMyThread
 
{
 ... UINT CMyThread::DoThreadJob()
public: {
  static UINT ThreadProc( LPVOID param );   // ... Can call member functions here
  }
private:  
  UINT DoThreadJob();
 
};
// Instance of the thread class
UINT CMyThread::ThreadProc( LPVOID param ) CMyThread myThread;
{  
  // Get the "this" pointer AfxBeginThread(
  CMyThread * pThis =   CMyThread::ThreadProc,
    reinterpret_cast<CMyThread *>(param);
  reinterpret_cast<LPVOID>( &myThread));
 
  // Route to non-static member function
  return pInstance->DoThreadJob();
}

 
 
CSI2520, Hiver 2007
Fils en Java
public class HelloRunnable implements Runnable {

public void run() {


System.out.println("Hello from a thread!");
} public class HelloThread extends Thread {
public static void main(String args[]) { public void run() {
(new Thread(new HelloRunnable())).start(); System.out.println("Hello from a thread!");
} }
} public static void main(String args[]) {
(new HelloThread()).start();
}

CSI2520, Hiver 2007


Contrôle des fils
HelloThread thread = HelloThread Fil();   

thread.start();   // demarrer le fil d’execution

thread.sleep( 3000 );   // mettre un fil en pause

thread.isAlive(); // verifier si un fil est actif

thread.interrupt();   // interrompre un fil

thread.join(); // attendre la terminaison d’un fil

CSI2520, Hiver 2007


Exemple
class Example extends Thread {
volatile boolean stop = false;

public static void main( String args[] ) throws Exception {


Example2 thread = new Example2();
System.out.println( "Starting thread..." );
thread.start();
Thread.sleep( 3000 );
System.out.println( "Asking thread to stop..." );
thread.stop = true;
Thread.sleep( 3000 );
System.out.println( "Stopping application..." );
System.exit( 0 );
}

public void run() {


while ( !stop ) {
System.out.println( "Thread is running..." );
sleep( 1000 );
}
System.out.println( "Thread exiting under request..." );
}
}

CSI2520, Hiver 2007


Niveaux de concurrence
• au niveau des énoncés: une série d' énoncés sont exécutés de façon
concurrente, le processus principal suspend alors son exécution. Chacun des
processus ainsi créés partagent le même ensemble de données (OpenMP)
• au niveau des sous-programmes: un processus commande alors la création
d’un autre processus dont la tâche consiste à exécuter un certain sous-
programme. Une fois le processus lancé, le processus appelant continue son
exécution. Un mécanisme de synchronisation doit toutefois être disponible.
• au niveau des objets: chacune des instances d' une classe devient une entité
concurrente; il y a alors exécution concurrente d' une de ses méthodes. Les
attributs ne sont pas partagés.
• au niveau des programmes: ceci se produit lorsqu' un processus parent a la
capacité de lancer un ou plusieurs processus enfant. Il doit toutefois exister un
moyen de connaître l' identité d' un processus. Les données peuvent être
partagées ou non.

CSI2520, Hiver 2007


Type de concurrence
• Physique: lorsque plusieurs processeurs se
partagent les différents processus
• Logique: lorsque plusieurs processus se
partagent le temps d' exécution sur un seul
processeur.
• Distribué: lorsque plusieurs machines
constituées en réseau se partagent les
processus.
CSI2520, Hiver 2007
Moniteur
• Un moniteur est une abstraction qui contient les données
partagées ainsi que les procédures qui accèdent à ces
données
• La synchronisation est alors réalisée implicitement en
n’autorisant qu’un accès à la fois au moniteur
• Lorsque le moniteur est occupé, tous les processus en
attente pour accès sont placés dans une file
• En réalisant un appel à une opération du moniteur, un
processus obtient des droits exclusifs d' accès aux
ressources du moniteur. Cette exclusivité demeure jusqu’à
ce que l’opération soit complétée ou jusqu’à ce que ce
processus se place en attente

CSI2520, Hiver 2007


Java et moniteurs
• En Java, tous les objets ont un moniteur
intrinsèque associé
– Méthodes synchronisées
– Blocs synchronisés
• avec l’énoncé synchronized(object)
• La file d’attente est gérée par la machine virtuelle
Java
• Jusqu’à 50 fois plus lente que les méthodes non-
synchronisées

CSI2520, Hiver 2007


Exemple: un bassin d’objets
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];

protected synchronized Object getNextAvailableItem() {


for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) { used[i] = true; return items[i]; }
}
return null; // not reached
}

protected synchronized boolean markAsUnused(Object item) {


for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) { used[i] = false; return true; } else return false;
}
}
return false;
}
}

CSI2520, Hiver 2007


Exemple:
Le producteur/consommateur
 private LinkedList list = new LinkedList();

  public void produce() {
    int len = 0;
    synchronized(list) { // impose un verrou sur la liste
      Object justProduced = new Object();
      list.addFirst(justProduced);
      len = list.size();
      list.notifyAll(); // avise les autres fils
    } // libere le verrou
    System.out.println("List size now " + len);
  }

CSI2520, Hiver 2007


Exemple:
Le producteur/consommateur
 public void consume() {
    Object obj = null;
    int len = 0;
    synchronized(list) {
      while (list.size() == 0) {
        try {
          list.wait(); // suspend le fil
        } catch (InterruptedException ex) {
          return;
        }
      }
      obj = list.removeLast();
      len = list.size();
    }
    System.out.println("Consuming object " + obj);
    System.out.println("List size now " + len);
  }

CSI2520, Hiver 2007


Sémaphore
• Une sémaphore est un mécanisme qui permet la
synchronisation et le partage de ressource entre processus

P(Semaphore s) // Acquire Resource


{
wait until s > 0, then s := s-1;
}
V(Semaphore s) // Release Resource
{
s := s+1;
}
Init(Semaphore s, Integer v) { s := v; }

CSI2520, Hiver 2007


Mutex
• Un mutex permet de verrouiller une
ressource pendant qu’un fil y accède
• Un mutex est donc associé à une ressource
(une classe)
• Implementés en utilisant des sémaphore
binaires

CSI2520, Hiver 2007


Exemple C++
CMutex mutex; // this is the Mutex class

// This is the synchronization class


// when you need to control access
// to one resource using a given mutex
CSingleLock mulock(&mutex);

if (mulock.Lock(1000)) { // will block until


// the Mutex is available

// exclusive access to this block


conteneur.erase(id);
}

mulock.Unlock(); // release the Mutex

CSI2520, Hiver 2007


Exemple: Accès exclusif
à un fichier
CMutex mutex; // one attribute
// of the class

void reader() { void writer() {

CSingleLock mulock(&mutex); CSingleLock mulock(&mutex);

if (mulock.Lock(1000)) { if (mulock.Lock(1000)) {

// do something… // do something…
read_file(); write_file();
} }

mulock.Unlock(); mulock.Unlock();
} }

CSI2520, Hiver 2007


Exemple: Accès à un fichier
import java.util.concurrent.Semaphore

• Lecture partagée const int MaxReaders = 32;


Semaphore semaphore(MaxReaders);
// MaxReaders is the total number of permits
– Il y a un nombre void ReaderThread::run()
maximum de fils {
semaphore.acquire(); // Acquires a permit from this semaphore,
pouvant lire // blocking until one is available;
read_file();
• Écriture exclusive }
semaphore.release(); // Release a permit

– Un seul fil doit écrire à void WriterThread::run() // should be synchronized


la fois {
// if more than one writer

– Le fil écrivant doit for (int i = 0; i < MaxReaders; ++i) semaphore.acquire();


// acquisition graduelle de ressource
attendre que tous les write_file();
semaphore.release(MaxReaders); // Release all permits
fils lisant terminent }

CSI2520, Hiver 2007


Attention aux ‘deadlocks ’ !
• Se produit lorsque deux fils vérouillent
chacun une ressource et veulent aussi
accéder à la ressource de l’autre
– Ex: Le diner des philosophes

CSI2520, Hiver 2007


Exemple
class Attention {

public synchronized void ceci(Attention obj) {

...
obj.cela(this);
}

public synchronized void cela(Attention obj) {


}
}

CSI2520, Hiver 2007