0
votes

I want to create a simple code which is able to generate odd and even numbers by using multi-threading. There are many ways of doing it, but I am trying to implement a lock, mainly for learning purposes. Here's my code:

from threading import *
from time import sleep

class Even:
    def __init__(self):
        self.c = Condition()

    def evenNumbers(self):
        self.c.acquire()
        for i in range(0, 101, 2):
            print("Even : ", i)
            self.num = i
            sleep(1.)
            self.c.notify()
            self.c.release()
            
            
class Odd:
    def __init__(self, even):
        self.even = even

    def oddNumbers(self):
        self.even.c.acquire()
        print("Odd : ", self.even.num+1)
        sleep(1.)
        self.even.c.notify()
        self.even.c.release()

tEven = Even()
tOdd = Odd(tEven)

t1 = Thread(target=tEven.evenNumbers)
t2 = Thread(target=tOdd.oddNumbers)

t1.start()
t2.start()

and I get the following error

Exception in thread Thread-6: Traceback (most recent call last): File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 926, in _bootstrap_inner self.run() File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 870, in run self._target(*self._args, **self._kwargs) File "", line 14, in evenNumbers self.c.notify() File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 346, in notify raise RuntimeError("cannot notify on un-acquired lock") RuntimeError: cannot notify on un-acquired lock

Any ideas?

2

2 Answers

1
votes

You forgot wait() calling. Because notify() wakes up only thread waiting for the condition variable.

from threading import *
from time import sleep


class Even:
    def __init__(self):
        self.c = Condition()

    def evenNumbers(self):
        self.c.acquire()
        for i in range(0, 101, 2):
            print("Even : ", i)
            self.num = i
            sleep(1.)
            self.c.notify()
            self.c.wait()
        self.c.release()


class Odd:
    def __init__(self, even):
        self.even = even

    def oddNumbers(self):
        self.even.c.acquire()
        for i in range(1, 100, 2):
            print("Odd : ", i)
            self.num = i
            sleep(1.)
            self.even.c.notify()
            self.even.c.wait()
        self.c.release()


tEven = Even()
tOdd = Odd(tEven)

t1 = Thread(target=tEven.evenNumbers)
t2 = Thread(target=tOdd.oddNumbers)

t1.start()
t2.start()
0
votes

The problem is that you have your call to acquire outside of the loop:

def evenNumbers(self):
    self.c.acquire()
    for i in range(0, 101, 2):
        print("Even : ", i)
        self.num = i
        sleep(1.)
        self.c.notify()
        self.c.release()

So that when you call release within the loop you are never calling acquire again. Move the call to acquire to be inside the loop:

def evenNumbers(self):
    for i in range(0, 101, 2):
        self.c.acquire()
        print("Even : ", i)
        self.num = i
        sleep(1.)
        self.c.notify()
        self.c.release()