Python threading giving unexpected behavior. Is this commonplace ?

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
LVL 1
beavoidSelf EmployedAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

gelonidaCommented:
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.
gelonidaCommented:
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 EmployedAuthor 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

Exploring SharePoint 2016

Explore SharePoint 2016, the web-based, collaborative platform that integrates with Microsoft Office to provide intranets, secure document management, and collaboration so you can develop your online and offline capabilities.

gelonidaCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
gelonidaCommented:
Does it work better now?

If not try to post a big enough example that we can run and reproduces the problem.
beavoidSelf EmployedAuthor Commented:
Thanks
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Python

From novice to tech pro — start learning today.