Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Threads sharing global variable

by Anonymous Monk
on Mar 02, 2016 at 10:40 UTC ( [id://1156640]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Trying a simple exercise as following: Problem: 3 threads accessing & manipulating a global variable. Conditions: Thread 1: should set the variable to a non-zero value if it sees the previous value as zero. Thread 2: should set the variable to zero if it sees a non-zero value. Thread 3: Simply prints the variable but, only when it changes from zero to non-zero or vice versa. It should not print 2 zeros or, same non-zero number twice. Sub-Conditions: The global variable will be set to 0 at the start. The threads should not step on each other. The non-zero values can be randomly generated My solution so far is:
use threads; use threads::shared; use Thread::Queue; my $a :shared; $a = 0; my $q = Thread::Queue->new(); sub set_positive { while (1) { lock $a; lock $q; if ($a == 0) { $a = int(rand() * 100); $q->enqueue($a); print "At set_positive: $a\n"; sleep(1); cond_broadcast($a); } else { cond_wait($a) } } } sub set_zero { while (1) { lock $a; lock $q; if ($a > 0) { $a = 0; $q->enqueue($a); print "At set_zero: $a\n"; sleep(1); cond_broadcast($a); } else { cond_wait($a) } } } sub printer { while (1) { lock $a; lock $q; my ($v_a, $v_b) = $q->dequeue(2); print "At printer $v_a $v_b ",$a, $/; sleep(1); } } my @threads = map threads->create($_), qw( set_positive set_zero print +er); $_->join for @threads;
My solution hits some racing condition due to which the program hangs. Your suggestions are appreciated

Replies are listed 'Best First'.
Re: Threads sharing global variable
by BrowserUk (Patriarch) on Mar 02, 2016 at 12:52 UTC

    There's no need to lock the queue handle; and you need to broadcast on both branches otherwise you've a race condition that is responsible for your hangs; and the sleeps are not useful.

    Try this:

    use threads; use threads::shared; use Thread::Queue; my $a :shared = 0; my $q = Thread::Queue->new(); sub set_positive { while (1) { lock $a; if ($a == 0) { $a = 1+int(rand() * 100); ## Modified 10/03/2016 15:57 in +accordance with 1156654 & 1157305 $q->enqueue($a); print "At set_positive: $a\n"; } else { cond_wait($a) } cond_broadcast($a); } } sub set_zero { while (1) { lock $a; if ($a > 0) { $a = 0; $q->enqueue($a); print "At set_zero: $a\n"; } else { cond_wait($a) } cond_broadcast($a); } } sub printer { while (1) { my ($v_a, $v_b) = $q->dequeue(2); print "At printer $v_a $v_b ",$a, $/; } } my @threads = map threads->create($_), qw( set_positive set_zero print +er); $_->join for @threads;

    Outputs:

    C:\test>1156640 At set_positive: 11 At set_zero: 0 At set_positive: 98 At set_zero: 0 At set_positive: 2 At set_zero: 0 At printer 11 0 0 At printer 98 0 94 At printer 2 0 94 At set_positive: 94 At set_zero: 0 At set_positive: 61 At printer 94 0 0 At set_zero: 0 At printer 61 0 0 At set_positive: 13 At set_zero: 0 At printer 13 0 0 At set_positive: 13 At set_zero: 0 At printer 13 0 0 At set_positive: 28 At set_zero: 0 At printer 28 0 0 At set_positive: 18 At set_zero: 0 At printer 18 0 0 At set_positive: 59 At set_zero: 0 At printer 59 0 0 At set_positive: 15 At set_zero: 0 At printer 15 0 0 At set_positive: 31 At set_zero: 0 At set_positive: 22 At printer 31 0 0 At printer 22 0 0 At set_zero: 0 At set_positive: 19 At set_zero: 0 At printer 19 0 0 At set_positive: 2 At set_zero: 0 At printer 2 0 0 At set_positive: 22 At set_zero: 0

    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.

      The signaling code is pretty awful. Lots of unnecessary locking and unlocking, and it's far more complicated than it needs to be. Here it is improved:

      use threads; use threads::shared; use Thread::Queue; my $a :shared = 0; my $q = Thread::Queue->new(); sub set_positive { lock $a; while (1) { cond_wait($a) while $a > 0; $a = int(rand() * 100) + 1; cond_signal($a); $q->enqueue($a); } } sub set_zero { lock $a; while (1) { cond_wait($a) while $a == 0; $a = 0; cond_signal($a); $q->enqueue($a); } } sub printer { while (1) { my ($v_a, $v_b) = $q->dequeue(2); print "At printer $v_a $v_b\n"; } } threads->create($_) for qw( set_positive set_zero printer); $_->join for threads->list;

      cond_wait should always be used as cond_wait($lock) while !desired_condition();.

      cond_signal (or cond_broadcast) is used when you change something checked by a thread's desired_condition().

      Remember, your lock is released when you call cond_wait, and re-obtained before cond_wait returns.

        What do you get when you run this program ikegami?

        I get one single line of output, i other words, it doesn't work

        Like BrowserUk says, you lock the variable in the outer scope, and that lock is never released -- not an improvement

        Lots of unnecessary locking and unlocking,

        And your version is sh*te! (Hint:locks should be held for as small a scope and for as little time as possible!). It may work for this specific do nothing, trivial example, but is entirely useless as a generic model of usage

        Threads that lock all their shared variables when they start and never release them. What a great idea. I wonder why nobody's thought of doing that before(*).

        And, as you you know, I'm not interested in your opinion (on anything), so feel free to feed your BS to the OP if you must, but stop polluting my day.

        *Oh right. Because it only makes sense in a lock-stepped latching scenario, and besides that real examples of the need for that are as rare a rocking horse droppings; it is a completely stupid way to use 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.
Re: Threads sharing global variable
by Anonymous Monk on Mar 02, 2016 at 13:36 UTC

    Note also that the expression int(rand() * 100) generates a number in the range 0..99 (inclusive). For non-zero value, you'll want to add one:

    $a = 1 + int rand 100;

Re: Threads sharing global variable
by Anonymous Monk on Mar 10, 2016 at 23:19 UTC

    Regarding the motivation of this exercise. Although I am not the original poster, I see there's a need to provide some further background.

    The readers-writers problem is an elementary problem in concurrency, and the typical solution involves the use of condition variables. The construct has its origins in 1974/1975 when Hoarse and Hansen formulated the idea of monitors.

    The Art of Multiprocessor Programming introduces this problem in Chapter 1. Their example has Alice and Bob place sentences on a billboard one letter at a time.

    "Both the mutual exclusion and producer-consumer protocols require waiting: if one participant is subjected to an unexpected delay, so is the other. ... Surprisingly, the readers-writers problem does have solutions that do not require waiting.

    The scope of this exercise is no doubt to provide a working demonstration based on the method of condition variables. Again, the wikipedia article should prove informative.

      The readers-writers problem is an elementary problem in concurrency, and the typical solution involves the use of condition variables. The construct has its origins in 1974/1975 when Hoarse and Hansen formulated the idea of monitors.

      Quoting ancient history serves no one any good. The world has move on -- a lot -- in the four decades since 1974/5. Especially with respect to concurrency and shared data.

      "Both the mutual exclusion and producer-consumer protocols require waiting:

      Read. Learn. Enjoy. Wait free algorithms.

      Some choice quotes:

      Wait-freedom is the strongest non-blocking guarantee of progress, combining guaranteed system-wide throughput with starvation-freedom. An algorithm is wait-free if every operation has a bound on the number of steps the algorithm will take before the operation completes.12 This property is critical for real-time systems and is always nice to have as long as the performance cost is not too high.
      Wait-free algorithms were rare until 2011, both in research and in practice. However, in 2011 Kogan and Petrank15 presented a wait-free queue building on the CAS primitive, generally available on common hardware. Their construction expanded the lock-free queue of Michael and Scott,16 which is an efficient queue often used in practice. A follow-up paper by Kogan and Petrank17 provided a methodology for making wait-free algorithms fast and used this methodology to make the wait-free queue practically as fast as its lock-free counterpart. A subsequent paper by Timnat and Petrank18 provided an automatic mechanism for generating wait-free data structures from lock-free ones. Thus, wait-free implementations are now available for many data-structures.

      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.

      I enjoyed the problem presented by the OP and also the history. Thank you for sharing.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1156640]
Approved by Ratazong
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2024-04-25 09:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found