Précédent Index Suivant

Exercices

Les philosophes débloqués

Pour résoudre l'éventuel blocage des philosophes orientaux, il suffit de limiter à quatre l'accès simultané à la table. Implantez cette solution. On rajoute un compteur indiquant le nombre de philosophes présents ainsi que deux fonctions, entrer et sortir, chargées respectivement de limiter les entrées et signaler les sorties.

#

let entrer, sortir =
let n = ref 0 in
let m = Mutex.create() in
let c = Condition.create() in
let loc_entrer () =
Mutex.lock m ;
while not (!n < 4) do Condition.wait c m done ;
incr n ;
if !n > 1 then Printf.printf "%d philosophes sont a table\n" !n
else Printf.printf "%d philosophe est a table\n" !n ;
flush stdout;
Mutex.unlock m
in
let loc_sortir () =
Mutex.lock m ;
decr n ;
Mutex.unlock m ;
Condition.broadcast c
in
loc_entrer, loc_sortir ;;
val entrer : unit -> unit = <fun>
val sortir : unit -> unit = <fun>
Il suffit alors d'appeler ces fonctions en début et en fin de boucle d'un philosophe.

# let philosophe i =
let ii = (i+1) mod 4 in
while true do
Printf.printf "Le philosophe (%d) se pointe\n" i ;
entrer () ;
mediter 3. ;
Mutex.lock b.(i);
Printf.printf
"Le philosophe (%d) prend sa baguette gauche et medite encore un peu\n" i;
mediter 0.2; Mutex.lock b.(ii) ;
Printf.printf "Le philosophe (%d) prend sa baguette droite\n" i ;
manger 0.5 ;
Mutex.unlock b.(i) ;
Printf.printf
"Le philosophe (%d) rend sa baguette gauche et commence deja a mediter\n" i;
mediter 0.15 ;
Mutex.unlock b.(ii) ;
Printf.printf "Le philosophe (%d) rend sa baguette droite\n" i ;
sortir () ;
Printf.printf "Le philosophe (%d) se tire\n" i ;
done ;;
val philosophe : int -> unit = <fun>
Attention, cette solution supprime les inter-blocages, mais pas les famines. Pour résoudre ce dernier problème, on peut soit se fier au hasard en introduisant un délai d'attente en aprés la sortie d'un philosophe, soit gérer explicitement une file d'attente.


La poste en fait plus

Nous proposons l'amendement suivant au bureau de poste décrit à la page ?? : certains clients impatients peuvent partir avant que leur numéro ne soit appelé.
  1. Ajouter à la classe distrib une méthode attendre (de type int -> unit) qui met en attente l'appelant tant que le dernier numéro distribué est inférieur ou égal au paramètre de la méthode (il faut modifier prendre de façon à ce qu'elle émette un signal).

    # class distrib () =
    object
    val mutable n = 0
    val m = Mutex.create ()
    val c = Condition.create ()

    method attendre nc =
    Mutex.lock m ;
    while (n <= nc) do Condition.wait c m done ;
    Mutex.unlock m

    method prendre () =
    Mutex.lock m ;
    n <- n+1 ;
    let nn = n in
    Condition.broadcast c ;
    Mutex.unlock m ;
    nn
    end ;;
    class distrib :
    unit ->
    object
    val c : Condition.t
    val m : Mutex.t
    val mutable n : int
    method attendre : int -> unit
    method prendre : unit -> int
    end


  2. Modifier la méthode attendre_arrivee de la classe guichet de façon à ce qu'elle retourne le booléen true si le client attendu s'est présenté et false si le client ne s'est pas présenté au bout d'un certain temps. On rajoute une méthode privée reveil chargée de réveiller le guichetier en attente au bout d'un temps donné.

    #
    method private reveil t =
    let dt = delai_attente_appel /. 10.0 in
    while (Unix.gettimeofday() < t) do Thread.delay dt done;
    Condition.signal c

    method attendre_arrivee () =
    let t = Unix.gettimeofday() +. delai_attente_appel in
    let r = Thread.create self#reveil t in
    Mutex.lock m;
    while libre && (Unix.gettimeofday() < t) do
    Condition.wait c m
    done;
    (try Thread.kill r with _ -> ());
    let b = not libre in (Mutex.unlock m; b)


  3. Modifier la classe affich en lui passant en paramètre un distributeur de numéros et en :
    1. ajoutant une méthode attendre_jusqu'a qui renvoie true si le numéro attendu a été appelé pendant un délai d'attente donné en paramètre et false sinon ; La modification de la méthode appel a pour but de garantir à la fois que ne sont appelés que des numéros effectivement distribués et que lorsqu'un numéro d'appel est affiché, il existe bien un guichet qui attend ce numéro.

      # class affich (d:distrib) =
      object
      val mutable nc = 0
      val m = Mutex.create ()
      val c = Condition.create ()

      method attendre n =
      Mutex.lock m ;
      while nc < n do Condition.wait c m done ;
      Mutex.unlock m

      method attendre_jusqu'a n t =
      Mutex.lock m ;
      while (nc < n) && (Unix.gettimeofday() < t) do Condition.wait c m done ;
      let b = not (nc < n) in
      Mutex.unlock m ;
      b

      method appel (g:guichet) =
      Mutex.lock m ;
      d#attendre nc ;
      nc <- nc+1 ;
      g#set_nc nc ;
      Condition.broadcast c ;
      Mutex.unlock m

      end ;;
      class affich :
      distrib ->
      object
      val c : Condition.t
      val m : Mutex.t
      val mutable nc : int
      method appel : guichet -> unit
      method attendre : int -> unit
      method attendre_jusqu'a : int -> float -> bool
      end


    2. modifiant la méthode appel pour qu'elle prenne en paramètre un guichet et mette à jour le champ nc de ce guichet (il faut rajouter une méthode de mise à jour dans la classe guichet).

  4. Modifier la fonction guichetier pour tenir compte des attentes infructueuses.

    # type bureau = { d: distrib; a: affich; gs: guichet array }
    val delai_service : float = 4
    val delai_arrivee : float = 2
    val delai_guichet : float = 0.5
    val delai_attente_client : float = 0.7

    let guichetier ((a : affich), (g : guichet)) =
    while true do
    a#appel g ;
    Printf.printf"Guichet %d appelle %d\n" g#get_ng g#get_nc ;
    if g#attendre_arrivee () then g#attendre_depart ()
    else
    begin
    Printf.printf"Guichet %d n'attend plus %d\n" g#get_ng g#get_nc ;
    flush stdout
    end ;
    Thread.delay (Random.float delai_guichet)
    done ;;
    val guichetier : affich * guichet -> unit = <fun>


  5. Écrire une fonction client_impatient qui simule le comportement d'un client impatient.

    # val chercher_guichet : 'a -> < get_nc : 'a; .. > array -> int = <fun>

    let client_impatient b =
    let n = b.d#prendre() in
    let t = Unix.gettimeofday() +. (Random.float delai_attente_client) in
    Printf.printf "Arrivee client impatient %d\n" n; flush stdout;
    if b.a#attendre_jusqu'a n t
    then
    let ig = chercher_guichet n b.gs in
    b.gs.(ig)#arriver() ;
    Printf.printf "Le client %d occupe le guichet %d\n" n ig ;
    flush stdout ;
    Thread.delay (Random.float delai_service) ;
    b.gs.(ig)#partir() ;
    Printf.printf "Le client %d s'en va\n" n
    else
    Printf.printf "Le client %d, las d'attendre, s'en va\n" n
    flush stdout ;;
    Characters 518-531:
    This function is applied to too many arguments

Producteurs et consommateurs d'objet

Cet exercice reprend l'algorithme des producteurs-consommateurs avec la variante suivante : le magasin de stockage est de taille finie (i.e. un tableau plutôt qu'une liste gérée en FIFO). De plus, nous proposons d'en faire une implantation utilisant, à l'instar du bureau de poste, les objets pour modéliser les ressources.
  1. Définir une classe produit de signature :

    # class produit (s:string) =
    object
    val nom = s
    method nom = nom
    end ;;
    class produit : string -> object val nom : string method nom : string end

    class produit : string ->
    object
    val nom : string
    method nom : string
    end


  2. Définir une classe magasin telle que :

    # class magasin n =
    object(self)
    val mutable taille = n;
    val mutable np = 0
    val mutable buffer = ([||] : produit array)
    val mutable ip = 0 (* Indice producteur *)
    val mutable ic = 0 (* Indice consommateur *)

    val m = Mutex.create ()
    val c = Condition.create ()

    initializer buffer<-Array.create n (new produit "empty")

    method display1 () =
    let i = ip mod taille in
    Printf.printf "Ajout (%d)%s\n" i ((buffer.(i))#nom)

    method deposer p =
    Mutex.lock m ;
    while (ip-ic+1 > Array.length(buffer)) do Condition.wait c m done ;
    buffer.(ip mod taille) <- p ;
    self#display1() ;
    ip <- ip+1 ;
    Mutex.unlock m ;
    Condition.signal c

    method display2 () =
    let i = ic mod taille in
    Printf.printf "Retrait (%d)%s\n" i ((buffer.(i))#nom)

    method prendre () =
    Mutex.lock m ;
    while(ip == ic) do Condition.wait c m done ;
    self#display2() ;
    let r = buffer.(ic mod taille) in
    ic<- ic+1 ;
    Mutex.unlock m ;
    Condition.signal c ;
    r
    end ;;
    class magasin :
    int ->
    object
    val mutable buffer : produit array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val mutable taille : int
    method deposer : produit -> unit
    method display1 : unit -> unit
    method display2 : unit -> unit
    method prendre : unit -> produit
    end

    class magasin : int ->
    object
    val mutable buffer : produit array
    val c : Condition.t
    val mutable ic : int
    val mutable ip : int
    val m : Mutex.t
    val mutable np : int
    val taille : int
    method deposer : produit -> unit
    method prendre : unit -> produit
    end
    Les indices ic et ip sont manipulés respectivement par les producteurs et les consommateurs. L'indice ic donne l'indice du dernier produit pris et ip celui du dernier produit stocké. Le compteur np donne le nombre de produits en stock. L'exclusion mutuelle et la mise en attente des producteurs et des consommateurs seront gérées par les méthodes de cette classe.

  3. Définir une fonction consommateur : magasin -> string -> unit.

    # let consommateur mag na =
    while true do
    let p = mag#prendre() in
    Printf.printf "Le consommateur %s prend le produit %s\n" na p#nom ;
    flush stdout ;
    Thread.delay(Random.float(3.0))
    done ;;
    val consommateur :
    < prendre : unit -> < nom : string; .. >; .. > -> string -> unit = <fun>


  4. Définir une fonction creer_produit de type string -> produit. Le nom donné à un produit sera composé de la chaîne passée en argument concaténée à un numéro de produit incrémenté à chaque appel de la fonction.
    Utiliser cette fonction pour définir producteur : magasin -> string -> unit.

    # let producteur =
    let num = ref 0 in
    let creer_produit () =
    let p = new produit("lessive-"^(string_of_int !num)) in
    incr num ;
    p
    in
    function mag -> function nm ->
    while true do
    let p = creer_produit () in
    mag#deposer(p) ;
    Printf.printf"Production de %s\n" p#nom ;
    flush stdout ;
    Thread.delay (Random.float (1.0))
    done ;;
    val producteur : < deposer : produit -> '_a; _.. > -> '_b -> unit = <fun>

Précédent Index Suivant