Python threading giving unexpected behavior. Is this commonplace ?

beavoid
beavoid used Ask the Experts™
on
Hi
My Python chess project new approach is tripping up.
I think it might be to do with Thread entanglement.
My engine instantiates a white and a black player, which are each abc's for their AI, random movers for now.

Then I start a game_loop() to handle the game play.

Does Thread "entanglement" ever happen? I cant see any reason for my Threads to unravel
I suspect this, because I have a thread counting the seconds going by on the console
( will help with clock)
But, when I instantiate my board window, the board window appears, but the counting thread disappears. (take away the # comment in ChessEngine.py to reinstate the board creation
Have they become entangled?

My
ChessEngine.py has the call to init the board commented out, so you can see the seconds being counted out.
as soon as the # is taken away and the board instantiates, the first Thread ceases to count.
It only counts the seconds when there is no board

Are Python Threads delicate and tough to combine?
Something in the board class is locking it all up
Code attached
What could it be?

Thanks

files...

ChessBoard.py

ChessEngine.py

ChessPlayer_abc.py

ChessBoard.py

RandomPlayer.py

ChessPlayer.py
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
I still didn't have time to look at all you code, but Just some general hints.

I'd strongly recommend to separate object creation and starting of a task.
In many cases you have much more freedom of choice when and how to start.
In the worst case you just have to type one line more (to start its thread)  per object.

So for example the Chess Engine I'd change:
class ChessEngine:

    def __init__(self):
        self.white_player = RandomPlayer() # will only create player, but not start it
        self.black_player = RandomPlayer() # will only create player, but not start it

        #self.board = ChessBoard()
       self.loop_thread = threading.Thread(target=self.game_loop)

    def start(self):
         # start threads in the correct order depeding on petential dependencies   
        self.white_player.start()
        self.black_player.start()
        self/loop+thread.start()

Open in new window



I would also make sure to use pygame only from one thread. I did not read the pygame documentation, but if you did not read explicitely, that pygame is thread safe, then I would not assume, that it is.

You could have one thread for the user interface and the displaying of the chess figures
and one thread for each player.
I didn't have time to all the files, but how did you implement inter thread communication / thread synchronisation?
multiple threads access the same variables you have to make sure, that it will never happen, that
- two threads try to modify the same object at the same time
- one thread is modifying an object, that another thread is reading.

Threading is very powerful, but very easy to get wrong.
Can you describe again the exact problem:

For me things see, to work as expected
import threading, time

from RandomPlayer import RandomPlayer
from ChessBoard import  ChessBoard


class ChessEngine:
    def __init__(self):
        self.white_player = RandomPlayer()
        self.black_player = RandomPlayer()
        self.board = ChessBoard()

        self.loop_thrd = threading.Thread(target=self.game_loop)

    def start(self):
        self.loop_thrd.start()

    def join(self):
        self.loop_thrd.start()

    # game loop
    def game_loop(self) :
        # each player's move must be made a Thread until a chosen move is made
        white_move_count=0
        while True:
            # white move section
            print('ChessEngine w8ing for white move #', white_move_count)
            white_move_count+=1
            time.sleep(1)


print('main')

engine = ChessEngine()
print("created engine")
engine.start()
print("started engine. waiting to finish")
engine.join()
print("engine finished")

Open in new window

beavoidSelf Employed

Author

Commented:
Hi
I've realized that this better reveals my issue:

In this code, I have a counting thread that prints out the second numbers as they pass
Two instances are eventually called, but only the first one displays.
Why is the second counter call not instantiated?
Thanks
import threading,time



class counter:


    def __init__(self,thread_number) :
        self.thread_number = thread_number

        t = threading.Thread(target=self.count())

        t.start()

        print('finished Thread init num:',thread_number)




    def count(self):

        frameCount = 0
        while True:
            print (self.thread_number,' frame #',frameCount)
            frameCount += 1
            time.sleep(1)


counter(1)
counter(2)

Open in new window

PMI ACP® Project Management

Prepare for the PMI Agile Certified Practitioner (PMI-ACP)® exam, which formally recognizes your knowledge of agile principles and your skill with agile techniques.

Didn't you see the answer to your other question?

just replace
         t = threading.Thread(target=self.count())

Open in new window

with
        t = threading.Thread(target=self.count)

Open in new window


you have to pass the function (the function object) that shall be called to  threading and not the result of the call to threading.Thread.

You have the same issue also in
t = threading.Thread(name="getEvents", target=self.getEvents())

Open in new window

where you should write
t = threading.Thread(name="getEvents", target=self.getEvents)

Open in new window


Just see it that way:
t = threading.Thread(target=self.count())

Open in new window

is equivalent to
       
result = self.count()   # this function will be called and runs in your case 
                        # infinitely and will therefore never return 
print("you will never reach this line")  # this line will never be reached
                              # if self.count() does not terminate
t = threading.Thread(target=result) # this line will never be reached
                          #if self.count() does not terminate

Open in new window


whereas

t = threading.Thread(target=self.count)

Open in new window

is equivalent to
function_to_call = self.count # you just get a reference to a function object, 
                            #but the function will NOT be called
t = threading.Thread(target=function_to_call) # now you create a thread and tell it that if it will be started
                                             # it shall call the function, that has been passed to the target parameter     

Open in new window

Does it work better now?

If not try to post a big enough example that we can run and reproduces the problem.
beavoidSelf Employed

Author

Commented:
Thanks

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial