Loading ...
Sorry, an error occurred while loading the content.

Basic examples for Thread library ?

Expand Messages
  • Fabrice Marchant
    Hi ! Please have you got some simple examples that demonstrate the use of Thread library ? I ve seen very few Ocaml things on this subject. Moreover, I ve
    Message 1 of 5 , Nov 2, 2007
    • 0 Attachment
      Hi !

      Please have you got some simple examples that demonstrate the use of Thread library ?
      I've seen very few Ocaml things on this subject. Moreover, I've never written anything based on threads in any language.
      Maybe I'm wrong but I bet threads can help to avoid an ugly 'forever loop' that circularly calls or polls a set of independant tasks ... and it would be overwhelming to program timers and interruptions to schedule differents tasks.

      Thanks for any link / example or advice.

      Fabrice
    • Richard Jones
      ... Danger Will Robinson! It s very hard to write threaded code correctly[1], they re hard to debug it when things inevitably go wrong, and on OCaml you don t
      Message 2 of 5 , Nov 2, 2007
      • 0 Attachment
        On Fri, Nov 02, 2007 at 11:46:37AM +0100, Fabrice Marchant wrote:
        > I've seen very few Ocaml things on this subject. Moreover, I've
        > never written anything based on threads in any language.

        Danger Will Robinson! It's very hard to write threaded code
        correctly[1], they're hard to debug it when things inevitably go
        wrong, and on OCaml you don't even get much real benefit from threads
        anyway.

        > Maybe I'm wrong but I bet threads can help to avoid an ugly 'forever
        > loop' that circularly calls or polls a set of independant tasks
        > ... and it would be overwhelming to program timers and interruptions
        > to schedule differents tasks.

        Sounds like a knotty problem. If the tasks are really independent,
        how about using separate processes instead of threads? This has the
        advantage that the tasks are isolated from each other, and can be
        restarted independently.

        Rich.

        [1] After reading a thread (no pun intended) I think on LKML recently,
        I'm now not even sure that it's possible to write correct threaded
        code in the multi-core case.

        --
        Richard Jones
        Red Hat
      • Fabrice Marchant
        Thanks Rich, On Fri, 2 Nov 2007 14:09:05 +0000 ... A bit like : - I ve such problem about a class. - - Are you sure you want to discover object-oriented
        Message 3 of 5 , Nov 2, 2007
        • 0 Attachment
          Thanks Rich,

          On Fri, 2 Nov 2007 14:09:05 +0000
          Richard Jones <rich@...> wrote:

          > Danger Will Robinson! It's very hard to write threaded code
          > correctly[1], they're hard to debug it when things inevitably go
          > wrong, and on OCaml you don't even get much real benefit from threads
          > anyway.
          A bit like : "- I've such problem about a class." ->
          "- Are you sure you want to discover object-oriented face of OCaml that is so complex and slow ?"
          If some smart people wrote this thread library, I just wonder if it wouldn't be valuable to try it...

          >> ...
          > Sounds like a knotty problem. If the tasks are really independent,
          > how about using separate processes instead of threads? This has the
          > advantage that the tasks are isolated from each other, and can be
          > restarted independently.
          Here are the actual things to program. There are :

          - a cellular automaton that behaves according to some rules applying on any data cells on a board,

          - a i/o module for the automaton,

          - a history module that keeps track of done actions,

          - a viewer that allows the user to look at the cellular automaton state from any point of view,

          - a controller that takes instructions from keyboard and mouse.
          These instructions are intended to control the four previous modules :

          * the cellular automaton :
          . progresses one step or
          . runs at full / specified speed or
          . stops any automatic execution.

          * the inputs / outputs :
          . clear the automaton board or
          . toggle a robot cell or
          . load a cell pattern from a file to the automaton or
          . save robot cells state to a file.

          * the history :
          . "undoes" one step or
          ."redoes" one step.

          * the viewer is able to :
          . observe the automaton anywhere or
          . zoom in / out or
          . change the skin of cells / board.

          ----------------------------------------------------
          This defined, please consider this example case :
          The user says, via the controller, to the automaton : "run at some speed". (Until a "stop" order appears)
          Meanwhile the user must be able to control the viewer module to change the point of view.
          So we should here consider that the run of the cellular robot and the changes of the viewer are independant tasks.
          ----------------------------------------------------
          But you spoke about 'separate processes' against threads.
          Maybe would it be the technique to use ?
          Please do you have more about this ?

          > [1] After reading a thread (no pun intended)
          I'm really fond of french puns : english ones are very difficult for french speaking people.
          However "These stunts are cunning and I kill these ones."

          Regards,

          Fabrice
          ----------------------------------------------------
          "What's the color of a chameleon put onto a mirror ? "Stewart Brand"
          "let f() = let rec g() = k() and k() = g() in k()" (* J.Skaller and V.Prevosto *)
          "Il est l'heure qu'il était hier à la même heure." Comme disent les enfants.
        • Richard Jones
          ... [etc] The core problem -- and a very common one -- is that you want to write a program that does some large amount of work (the cellular automaton) but
          Message 4 of 5 , Nov 3, 2007
          • 0 Attachment
            On Fri, Nov 02, 2007 at 10:22:43PM +0100, Fabrice Marchant wrote:
            > On Fri, 2 Nov 2007 14:09:05 +0000
            > Richard Jones <rich@...> wrote:
            > > Sounds like a knotty problem. If the tasks are really independent,
            > > how about using separate processes instead of threads? This has the
            > > advantage that the tasks are isolated from each other, and can be
            > > restarted independently.
            > Here are the actual things to program. There are :
            >
            > - a cellular automaton that behaves according to some rules applying on any data cells on a board,
            >
            > - a i/o module for the automaton,
            >
            > - a history module that keeps track of done actions,
            >
            > - a viewer that allows the user to look at the cellular automaton state from any point of view,
            >
            > - a controller that takes instructions from keyboard and mouse.
            > These instructions are intended to control the four previous modules :
            >
            > * the cellular automaton :
            > . progresses one step or
            > . runs at full / specified speed or
            > . stops any automatic execution.
            [etc]

            The core problem -- and a very common one -- is that you want to write
            a program that does some large amount of work (the cellular automaton)
            but needs to be responsive to keypresses and other events from
            outside.

            If your program didn't need to respond to the outside events then the
            usual way to structure such a program would be:

            let state = load_initial_state () in
            let rec loop state =
            let new_state = do_work state in
            loop new_state (* infinite loop *)
            in
            loop state

            where 'do_work' does some defined amount of work such as a single step
            in the simulation. In a typical simulation, 'do_work' takes some
            unpredictable amount of time (perhaps 1/100th of a second or 100
            seconds) and is hard to split up.

            Compare that to the usual form for programs which respond to events:

            let rec loop () =
            let key = get_key () in
            do_action key;
            loop ()
            in
            loop ()

            where 'do_action' carries out the action commanded by the user, and we
            expect that either actions are short or else the user will wait for
            the action to complete.

            Now of course what we want is a program that can do both of the above,
            and under Windows the natural expression of this would be to use two
            threads. That's dangerous though because the two threads are running
            independently of each other but need to synchronise up somehow. For
            example, if the event thread gets a "quit" command from the user, it
            needs to tell the simulation thread to stop. It might do this by
            setting a global variable called quit, and then we could change the
            simulation loop by adding the extra code below:

            let state = load_initial_state () in
            let rec loop state =
            let new_state = do_work state in
            if not !quit then loop new_state
            ^^^^^^^^^^^^^^^^^ (* keep looping unless quit variable is set *)
            in
            loop state

            It seems OK, but in fact it won't work on OCaml, and even in C it
            contains a bug because the quit variable really should be locked to
            prevent both threads trying to access it at the same time. (Consider
            what would happen if the command was something more complicated such
            as "restart the simulation from this complicated initial state" -- you
            definitely wouldn't want the simulation thread seeing a half-
            constructed initial state which was still being built by the event
            thread).

            It doesn't work at all in OCaml for a more fundamental reason: if the
            simulation doesn't do something like the occasional bit of I/O, then
            the simulation thread will run continuously, and the event thread will
            never run at all.

            Under Unix you have alternatives. One is to fork a subprocess to run
            the simulation, and to send it signals:[1]

            let pid = Unix.fork () in
            if pid = 0 then (
            (* Child process, simulation code goes here ... *)

            ) else (
            (* Event loop code here ... *)

            (* Send an event to the simulation: *)
            Unix.kill pid Sys.sigusr1
            )

            The child process can catch these signals asynchronously (see:
            Sys.set_signal) and act on them, but the range of signals is small,
            there are only a handful of signal numbers available to use, and so
            the number of messages you can send is correspondingly restricted.

            Another Unix way is to make a pipe between parent and child process
            (see: Unix.pipe). You can send whole messages over the pipe of any
            complexity you like (eg. using the functions in Marshal to serialise
            and deserialise your messages). A pipe has the advantage that a
            message is always reassembled fully (no half-constructed messages).
            You also have a well-defined interface between your event code and
            your simulation code, and memory protection. And there are various
            mechanisms that the child can use to check for new messages coming
            over the pipe: for example by setting up the operating system to send
            you a signal, or by regularly polling the pipe using Unix.select.

            (All of the above is explained in W. Richard Stevens book).

            There are a bunch of other inter-process communications (IPC)
            mechanisms available to you. Amongst others:

            - simulation runs as a daemon listening on a socket for commands

            - several types of shared memory for communication, all of which
            have similar safety problems to threads although less severe

            - RPC mechanisms such as SunRPC sometimes can be used asynchronously

            Rich.

            [1] Notice a clever thing here: If you can fork one subprocess, then
            why not fork several subprocesses? Each will run on a separate core
            in a multi-core system. Furthermore unlike threads there won't be any
            memory contention possible between them, so they'll run at full speed
            on a NUMA system. Think NUMA systems are rare? I have two under my
            desk at work. Any AMD multicore machine built in the last year or two
            is a NUMA system, and as the number of cores increases further, NUMA
            is bound to become the standard.

            --
            Richard Jones
            Red Hat
          • Oliver Bandel
            ... [...] You made some suggestions... ...let me add my comment: When you use different processes, commands can be written to the automaton/graphics-machine
            Message 5 of 5 , Nov 3, 2007
            • 0 Attachment
              Zitat von Richard Jones <rich@...>:

              > On Fri, Nov 02, 2007 at 10:22:43PM +0100, Fabrice Marchant wrote:
              > > On Fri, 2 Nov 2007 14:09:05 +0000
              > > Richard Jones <rich@...> wrote:
              > > > Sounds like a knotty problem. If the tasks are really independent,
              > > > how about using separate processes instead of threads? This has the
              > > > advantage that the tasks are isolated from each other, and can be
              > > > restarted independently.
              > > Here are the actual things to program. There are :
              > >
              > > - a cellular automaton that behaves according to some rules applying on any
              > data cells on a board,
              > >
              > > - a i/o module for the automaton,
              > >
              > > - a history module that keeps track of done actions,
              > >
              > > - a viewer that allows the user to look at the cellular automaton state
              > from any point of view,
              > >
              > > - a controller that takes instructions from keyboard and mouse.
              > > These instructions are intended to control the four previous modules :
              > >
              > > * the cellular automaton :
              > > . progresses one step or
              > > . runs at full / specified speed or
              > > . stops any automatic execution.
              > [etc]
              >
              > The core problem -- and a very common one -- is that you want to write
              > a program that does some large amount of work (the cellular automaton)
              > but needs to be responsive to keypresses and other events from
              > outside.
              [...]


              You made some suggestions...

              ...let me add my comment:


              When you use different processes, commands can be written
              to the automaton/graphics-machine per pipe.

              It also would be possible to use such a separated process
              from the shell. If you than do NOT use marshaleöld data, but an
              ASCII-based protzocoll and have a slim language parser inside
              the graphics-machine/automaton, then you can directly type
              the commands from the shell.

              For that you have to use STDIN as the channel of choice
              for reading the commands.
              No nested memory areas, but onky reading from STDIN.

              If you start that process from the shell, you can use it
              and type the commands by hand.
              Or if you start it from the main-program, the pipe can be
              connected to it, so that the commands can be written from one process to the
              command-readeing process.

              So, what does this mean, comparing Threads and Processes?

              It means: Processes do not only bring you the advantage of
              separated (and more safe) operation.

              They also can enhance the flexibility of the whole application
              a lot, because you naturally have a kind of modularization!

              For making really flexible applications, this is much better.
              You can use the Unix-Toolbox together with your software,
              and instead of rearranging the code, you can write some shell-scripts to
              make a system that behaves different.

              IMHO, this flexibility is one of the major advantages of
              using processes.

              Ciao,
              Oliver


              P.S.: different simulations could be started from the shell,
              and a GUI, if necessary, can be added later.
              And if sockets are in use, the program also could be
              controlled from a remote host.
              This adds another flexibility. If you want to change the
              GUI in a threaded monolithic application, it means rewriting
              the application.
              If you already have separated programs, that communicate
              via pipes/sockets, you can change it, without touching the code
              of the automaton again!
            Your message has been successfully submitted and would be delivered to recipients shortly.