Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Weird Output with Threads and NCurses

by var121 (Novice)
on Jul 17, 2016 at 05:16 UTC ( #1167900=perlquestion: print w/replies, xml ) Need Help??
var121 has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks, I am learning Curses and Threads, as a simple exercise I have written below code to update a dummy counter at a given row. Program works fine for some time and suddenly the terminal screen starts displaying all weird characters. Please help me to understand the concept I have missed.
use threads; use Curses; use strict; initscr(); curs_set(0); box( ACS_VLINE, ACS_HLINE ); my $lthr = threads->create(\&counter, 5); my $rthr = threads->create(\&counter, 15); sub counter { my $pos = shift; #Position:Row my $lctr = 0; while(1) { $lctr++; move($pos,5); addstr("$lctr"); napms(100); refresh(); } } $lthr->join(); $rthr->join(); getch(); endwin(); exit(0);

Replies are listed 'Best First'.
Re: Weird Output with Threads and NCurses
by BrowserUk (Pope) on Jul 17, 2016 at 05:40 UTC

    I don't use *nix; and the last time I used curses, was very brief & last century; but the idea that it would work correctly being accessed concurrently from multiple threads seems ... very remote.

    As with most GUI frameworks that weren't developed specifically to work with threading; if you have to use curses, use it from one thread only and route all console IO from other threads, through that CUI thread via a queue.

    Attempting to get anything else to work is a waste of your time; and is very unlikely to receive anything by way of support.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.
      perldoc perlthrtut, says that Boss/Worker model is common in GUI applications. Can you kindly elaborate your comment, I didn't understand parts of it. I am new to Threads concepts.

        See Re: Tk, threads, and mjpeg stream or any of any of the many threads turned up by this search for examples and discussion of using Tk and threads.

        I cannot offer anything that uses curses directly as I don't use it, it doesn't run on my platform and whatever transient knowledge I had of it has long since been archived into long-term write-only storage :)


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Weird Output with Threads and NCurses
by Marshall (Abbot) on Jul 17, 2016 at 16:57 UTC
    You say I am new to Threads concepts. I will talk about the generic problem using an analogy which I hope is not too simplistic. I don't use curses and I come from a hardware background, but I hope this will make sense.

    Conceptually there is only one screen. Typical operations might be move to X, display Y (move, display). Let's say that you have a couple of threads running that are doing this same sequence of steps.

    In the thread environment, these threads could be (and probably are) running on separate cores. A dual core machine is very much like two computers that share a common memory. The cores operate independently and the programs (threads) run asynchronously (no timing relationship to each other).

    Lets say we have thread A,B talking to this single display. moveA,DisplayA,moveB,displayB..no problem with that sequence. However, I think we can see that if the sequence that the display sees winds up being: moveA,moveB,displayA,displayB, there is a problem! Remember that A and B are completely independent of each other and this could happen. We get the wrong stuff displayed in the wrong place.

    The actual situation is even worse than that. This move operation is not a single thing. Underneath it, work is going on to update various counters and pointers related to the single display. What happens if we are doing MoveB and before MoveA even finishes, the MoveB operation starts? Oh, geez are we in trouble! It is highly likely that whatever internal state that "move" is maintaining it is going to get screwed up and we don't wind up with either a "clean" A or B positioning.

    As BrowserUk points out, the best solution here is for you to enforce serialization of the display commands by having only a single thread giving out these move,display commands. Some parts of the system, like the file/disk system are designed so that this is not necessary (it does the serialization to the actual hardware with some non-trivial code). The GUI is not designed that way, so you have to do that.

    I don't find it surprising that your code runs for awhile before it fails. The threads will "drift" in time relationship to each other. If for no other reason that the OS needs to do other things and will "steal" some time from one of the cores to do it. If the operations are fast and relatively widely spaced out, it might take awhile for disaster to strike. But it inevitably will.

    I hope that I didn't confuse the issue more and apologize for the wordy post.

      Thanks for the wordy post , I understood the issue better now. Do we need threads If we serialize a move operation ? We can achieve the similar behaviour with a simple code below,
      @positions = qw/5 15/; while (1) { for (@positions) { move($_, CONSTANT); addstr($Counter); $Counter++; } }
      But the  $Counter is same and we will have to write little complex code to use multiple counters. Then where do we really use threads in GUI Applications ?
        where do we really use threads in GUI Applications ?

        behind the curtain

        That is to say, when the GUI receives a key or button press from the user, it hands the (possibly lengthy) action off to a "background" thread, so that meanwhile the user can immediately prepare (and initiate) the next action without having to gnaw fingernails or knuckles :-)
        Not using threads at all is certainly one of the options. The code that you have looks fine to me. There could wind up with the need for a worker thread to do something and that is fine as long as it is not talking directly to the gui. Glad to hear that you understand the issue now.
Re: Weird Output with Threads and NCurses
by Anonymous Monk on Jul 18, 2016 at 14:59 UTC

    The advice I've seen from Freenode's #perl is "Don't use threads, use an Async library instead".

    From perlbot: async "Asynchronous event-driven IO is awesome in Perl with POE, Future/IO::Async, IO::Lambda, Mojo::IOLoop, Reflex, or Promises, among others."

    Perhaps some monks more familiar with the above modules can implement your example in them.

    I've done your problem with my own Async::Tiny and it's approximately the same length and works without any weird problems. I can show you if you're interested.

      Even if var121 doesn't show interest, I think it's always good to show and compare multiple implementations of the same problem using different approaches and/or libraries.

      Using an asynchronous library is usually "cheating" in the sense that you don't get the nasty concurrency problems that threads produce, but I consider that the advantage of using an asynchronous approach. The downside usually is that you need to taylor all your code towards the asynchronous operation instead of basically writing synchronous code that then is run in a separate thread.

      By popular (at least one) request:

      #!/usr/bin/perl # http://perlmonks.org/?node_id=1167900 use Async::Tiny; # http://72.211.166.141:8080/async-tiny.tgz use Curses; use strict; use warnings; my $t = Async::Tiny->new; $t->addReadCallback( *STDIN, sub { die } ); $t->changeReadMode( *STDIN, 'character' ); $t->addRepeatCallback( 0.100, \&counter, 5, [0] ); $t->addRepeatCallback( 0.110, \&counter, 15, [1000] ); sub counter { my ($pos, $statearrayref) = @_; move $pos, 5; addstr ++$statearrayref->[0]; refresh; } initscr(); clear; curs_set 0; box( ACS_VLINE, ACS_HLINE ); eval { $t->eventloop }; endwin();

      And here's Async::Tiny

        By way of contrast, here's what a threaded solution might look like:

        #! perl -slw use strict; use threads; use Thread::Queue; use Win32::Console; my $con = new Win32::Console STD_OUTPUT_HANDLE; $con->Cls( $FG_LIGHTBLUE | $BG_WHITE ); my $Qcon = new Thread::Queue; async{ while( my( $what, $where ) = split $;, $Qcon->dequeue ) { $con->WriteChar( $what, 5, $where ); } }->detach; sub writer { my $pos = shift; my $counter = 0; while( 1 ) { $Qcon->enqueue( join $;, ++$counter, $pos ); select '','','', rand( 0.2 ); } } my @writers = map threads->create( \&writer, $_ ), 5, 15; sleep 100;

        It is windows only, but adapting it to *nix shouldn't be too hard.

        The real benefit shows up when you start to want to do something useful in the threads.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.
        > And here's Async::Tiny

        It shouldn't be here, it should be there!

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        Thanks for your post, I will go through the concept of Asynchronous programming. What is the last argument in  addRepeatCallback call ? Does First argument define the time ? and we are assuming the first call will be completed in 0.010 time units ? For writing games, where we will have to do many things simultaneously, like keeping score, updating screen, is that possible with Async Technique ? I have this doubt because I have seen some simple game code, written using threads, and it works absolutely fine.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1167900]
Approved by Corion
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (10)
As of 2018-07-20 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (435 votes). Check out past polls.

    Notices?