AssertionError: group argument must be None for now

I start to learn python and multithreading. I am trying to do the dining philosopher problem. I use class philosophers(threading.thread). But when I put in some sample and create threads, it has error AssertionError: group argument must be None for now.

import threading
import time
from random import randint

exitFlag = 0

class philosophers(threading.Thread):
    
    def __int__(self, phi, hungry, eat):
        threading.Thread.__int__(self)
        #super(philosophers.self)__int__()
        self.phi = phi
        self.hungry = hungry
        self.eat = eat
        
    def hungry(self):
        time.sleep(self.hungry)
        print("Philosopher" +(self.phi) + "is hungry and want to eat" + "n")
        startEat()
        
    def startEat(self):
        left = self.phi
        right = (self.phi+1)%5
        left.acquire()
        if right.locked():
            left.release()
        else:
            right.acquire()
        time.sleep(self.eat)
        
        right.release()
        left.release()
        print("Philosopher" +(self.phi) + "is finished" + "n")

#def run()
thread1 = philosophers(1, 5, 3)
thread2 = philosophers(2, 4, 2)
#thread3 = philosophers(3, 6, 7)
#thread4 = philosophers(4, 2, 8)
#thread5 = philosophers(5, 3, 4)

thread1.start()
thread2.start()
#thread3.start()
#thread4.start()
#thread5.start()

thread1.join()
thread2.join()
#thread3.join()
#thread4.join()
#thread5.join()

Answer

Your main problem is simple typo: it has to be __init__ instead of __int__ (in two places)

But there are other probles.

When you execute thread.start() then it create thread in system and executes thread.run() so you have to rename def hungry into def run

left and right are normal numbers int. You can’t use them with acquire, locked, release. You need objects threading.Lock for this.

It is not mistake but you could keep threads on list and then it is simpler to use start() and join() on all threads. And you don’t have to change this part of code even if you change number of threads on list.

PEP 8 — Style Guide for Python Code


Last problem is that I’m not sure if your algorithm is correct.

As for me Philosopner can eat (time.sleep(self.eat)) even if he doesn’t have both forks.

And at start left.acquire() can give False but you don’t check it.

EDIT:

I added more print() to see values in variable and which part of code is executed and I get:

Philosopher 4 is hungry and want to eat
Philosopher 4 left fork: True
Philosopher 4 right fork: True
Philosopher 4 is eating
Philosopher 5 is hungry and want to eat
Philosopher 2 is hungry and want to eat
Philosopher 2 left fork: True
Philosopher 2 right fork: True
Philosopher 2 is eating
Philosopher 1 is hungry and want to eat
Philosopher 1 left fork: True
Philosopher 1 doesn't have forks
Philosopher 3 is hungry and want to eat
Philosopher 2 is finished
Philosopher 3 left fork: True
Philosopher 3 doesn't have forks
Philosopher 1 is finished
Philosopher 4 is finished
Philosopher 5 left fork: True
Philosopher 5 right fork: True
Philosopher 5 is eating
Philosopher 3 is finished
Philosopher 5 is finished

and there are 1 doesn't have forks, 3 doesn't have forks but they finish without eating.

It may need some loop to try again get forks.


import threading
import time


class Philosopher(threading.Thread):  # PEP8: `UpperCaseNames` for classes.
                                      # PEP8: This class describes single Philospher so it should have name without `s` at the end
    
    def __init__(self, phi, hungry, eat, locks): #, *args, **kwargs):
        super().__init__() #*args, **kwargs)
        self.phi = phi
        self.hungry = hungry
        self.eat = eat
        self.locks = locks

    def run(self):
        time.sleep(self.hungry)
        print(f"Philosopher {self.phi} is hungry and want to eat")
        self.start_eat()  # need `self.`
        
    def start_eat(self):
        left = self.locks[self.phi-1]   # Python uses indexes `[0..4]` instead of `[1..5]`
        right = self.locks[self.phi%5]  # Python uses indexes `[0..4]` instead of `[1..5]`
        
        result = left.acquire()
        print(f"Philosopher {self.phi} left fork:", result)

        if right.locked():
            left.release()
            print(f"Philosopher {self.phi} doesn't have forks")
        else:
            result = right.acquire()            
            print(f"Philosopher {self.phi} right fork:", result)
            print(f"Philosopher {self.phi} is eating")
        time.sleep(self.eat)
        
        if right.locked():
            right.release()
        if left.locked():
            left.release()
        
        print(f"Philosopher {self.phi} is finished")


# --- main ---

# 5 locks for 5 Philosophers
locks = [threading.Lock() for _ in range(5)]

threads = [
    Philosopher(1, 5, 3, locks),
    Philosopher(2, 4, 2, locks),
    Philosopher(3, 6, 7, locks),
    Philosopher(4, 2, 8, locks),
    Philosopher(5, 3, 4, locks),
]    

for t in threads:
    t.start()

for t in threads:
    t.join()

EDIT:

It seems this algorithm works correctly.

def start_eat(self):
    left = self.locks[self.phi-1]   # Python uses indexes `[0..4]` instead of `[1..5]`
    right = self.locks[self.phi%5]  # Python uses indexes `[0..4]` instead of `[1..5]`

    while True:    
        if not left.locked():    
            result = left.acquire()
            #print(f"Philosopher {self.phi} left fork:", result)
            if right.locked():
                left.release()
                #print(f"Philosopher {self.phi} doesn't have forks")
            else:
                result = right.acquire()            
                #print(f"Philosopher {self.phi} right fork:", result)
                print(f"Philosopher {self.phi} is eating")
                time.sleep(self.eat)
                right.release()
                left.release()
                print(f"Philosopher {self.phi} is finished")
                break

Result:

Philosopher 4 is hungry and want to eat
Philosopher 4 is eating
Philosopher 5 is hungry and want to eat
Philosopher 2 is hungry and want to eat
Philosopher 2 is eating
Philosopher 1 is hungry and want to eat
Philosopher 3 is hungry and want to eat
Philosopher 2 is finished
Philosopher 1 is eating
Philosopher 1 is finished
Philosopher 4 is finished
Philosopher 5 is eating
Philosopher 3 is eating
Philosopher 5 is finished
Philosopher 3 is finished