invictus1306
invictus1306

Reputation: 587

Consumer / producer does not wait for event

I want write a program producer/consumer, in this program I have a parent and a son, the parent fills a shared variable with a number of fish and it send notify at son. The son start eating, if there are not fish, it notify parent. I tried this code, but it not working:

import threading
import time

NUM_FISH = 13

mutex = threading.Lock()
mutParent = threading.Event()
mutSon = threading.Event()

fish = NUM_FISH

def set(fish1):
    global fish
    fish = fish1

def get():
    return fish

def parent(mutParent, mutSon):
    while True:
                mutex.acquire()
                mutParent.wait()
                time.sleep(0.5)
                try:
                    set(NUM_FISH)
                    print " + parent brings %d fish\n" % fish             
                    mutex.release()
                    mutSon.set()
                except:
                    print "Exception"
                    mutex.release()

def son(id, mutParent, mutSon):
    while True:
                mutex.acquire()
                mutSon.wait()
                fish = get() - 1
                set(fish)
                time.sleep(0.5)
                try:
                    if fish > 0 :
                        print " - Son %d eats (dish: %d fish)\n" % (id, fish)
                        mutex.release()
                    else:
                        print " - Son %d eats (dish: %d fish) and screams\n\n" % (id, fish)
                        mutex.release()
                        mutParent.set()
                except:
                    print "Exception"
                    mutex.release()

print "\n + intitial dish: %d fish\n\n" % fish

mutSon.set()
t2 = threading.Thread(target=son, args=(1, mutParent, mutSon))
t2.start()

t1 = threading.Thread(target=parent, args = (mutParent, mutSon))
t1.start()

t2.join()
t1.join()

This is my output:

myself@ubuntu:~/Desktop$ python a.py

 + intitial dish: 13 fish


 - Son 1 eats (dish: 12 fish)

 - Son 1 eats (dish: 11 fish)

 - Son 1 eats (dish: 10 fish)

 - Son 1 eats (dish: 9 fish)

 - Son 1 eats (dish: 8 fish)

 - Son 1 eats (dish: 7 fish)

 - Son 1 eats (dish: 6 fish)

 - Son 1 eats (dish: 5 fish)

 - Son 1 eats (dish: 4 fish)

 - Son 1 eats (dish: 3 fish)

 - Son 1 eats (dish: 2 fish)

 - Son 1 eats (dish: 1 fish)

 - Son 1 eats (dish: 0 fish) and screams


 - Son 1 eats (dish: -1 fish) and screams


 - Son 1 eats (dish: -2 fish) and screams


 - Son 1 eats (dish: -3 fish) and screams


 - Son 1 eats (dish: -4 fish) and screams


 - Son 1 eats (dish: -5 fish) and screams


 + parent brings 13 fish

 + parent brings 13 fish

Upvotes: 3

Views: 129

Answers (1)

matsjoyce
matsjoyce

Reputation: 5844

OK, there are three things here that can be changed:

  1. This one is sort of cosmetic. Use with mutex: instead of all the mutex.acquire() and mutex.release(), as then that stuff happens automatically, making the code shorter and less error prone.
  2. You should wait for your events before acquiring mutex. Otherwise a thread will acquire the mutex, then begin to wait for its condition variable, however, the tread that is supposed to set it can't acquire mutex, so everything stops. Note that, when there is multiple sons or parents, the event has to be rechecked after locking mutex. This is because after waiting for the event, the event may be cleared before mutex has been acquired.
  3. After waiting for an event, you should act on the event, then clear it. Otherwise, when the son thread sets the event, the parent wakes up and deals with it. However, as the event is still set, if the parent wakes up again, it proceeds again, giving the double parent lines (and double son sections).

Making those adjustments gives me this code:

def parent(id, mutParent, mutSon):
    while True:
        mutParent.wait()
        with mutex:
            if not mutParent.is_set():
                continue
            time.sleep(0.5)
            try:
                set(NUM_FISH)
                print " + Parent %d brings %d fish\n" % (id, fish)
                mutParent.clear()
                mutSon.set()
            except:
                print "Exception"

def son(id, mutParent, mutSon):
    while True:
        mutSon.wait()
        with mutex:
            if not mutSon.is_set():
                continue
            fish = get() - 1
            set(fish)
            time.sleep(0.5)
            try:
                if fish > 0:
                    print " - Son %d eats (dish: %d fish)\n" % (id, fish)
                else:
                    print " - Son %d eats (dish: %d fish) and screams\n\n" % (id, fish)
                    mutSon.clear()
                    mutParent.set()
            except:
                print "Exception"

I haven't managed to break this (works with multiple sons or parents), so I think its correct, but any correction on this point is most welcome (as this is multithreading, and weird things lie in the shadows of parallelism).

Upvotes: 4

Related Questions