Python stuck in a single thread of a multi-threaded program
Question: I'm currently writing a program that is attempting to synchronize a visitor,
car, pump, and gas station thread at a zoo where guests arrive, wait for an
available car, take a tour, then exit, the cars must refuel every 5 rides, and
the gas station must refuel every time 3 cars are refilled. My tests are with
35 visitors, 6 cars, 5 gas pumps, and a time interval of 2. The program reads
a text file with the number of guests, cars, pumps, and a time interval
between visitors boarding vehicles, then uses the data to fill a class. I
create methods for each thread, then create the threads themselves in main. My
problem is that my program gets stuck at 6 vehicles, which is the number
specified by the text file, after running my first thread method,
visitor_thread, and I cannot tell if any other threads are even running
concurrently. I am a total novice at multithreading and python alike, and I'm
not sure what the problem is. I have worked on the project for twelve hours
straight and have been stuck at this point for the past four hours. In theory,
visitor_thread, car_thread, and gas_station_thread should all run concurrently
from main, and visitor_thread should have vehicles to work with again after
some visitors finish their ride, but I just get stuck with six full cars in an
infinite loop within visitor_thread. What is causing this infinite loop and
how can I make sure all of my threads are actually running?

My code:

    
    
    from threading import Thread
    from threading import Lock
    from threading import Event
    
    event = Event()
    lock = Lock()
    done = Event()
    is_done = 0
    
    
    class Arguments:
    def __init__(self, m_visitors, n_cars, k_pumps, t_time, thread_num, _done):
        self.m_visitors = m_visitors
        self.n_cars = n_cars
        self.k_pumps = k_pumps
        self.t_time = t_time
        self.thread_num = thread_num
        self.done = _done
    
    
    class Car:
    def __init__(self, control, rides, time, in_service, int_cus, in_queue):
        self.control = control
        self.rides = rides
        self.time = time
        self.in_service = in_service
        self.int_cus = int_cus
        self.in_queue = in_queue
    
    
    class Pump:
    def __init__(self, control, in_use, car_num, time):
        self.control = control
        self.in_use = in_use
        self.car_num = car_num
        self.time = time
    
    
    class PumpQueue:
    def __init__(self, pQueue, MAXsize, front, back, size):
        self.q = Lock()
        self.pQueue = pQueue
        self.MAXsize = MAXsize
        self.front = front
        self.back = back
        self.size = size
    
    
    def visitor_thread(_visitor):
    global is_done
    visitor = _visitor
    
    v = visitor.m_visitors
    c = visitor.n_cars
    p = visitor.k_pumps
    t = visitor.t_time
    id = visitor.thread_num
    
    i = 0
    j = 0
    while i < v:
        for j in range(0, c):
            lock.acquire()
            if cars[j].in_service is False and cars[j].rides < 5:
                print('\nVisitor %d is currently in car %d' % (i+1, j+1))
                cars[j].in_service = True
                i += 1
                print('\n%d customers waiting for a ride.' % (v - i))
                lock.release()
                break
            lock.release()
    lock.acquire()
    is_done += 1
    lock.release()
    
    
    def car_thread(_car):
    global is_done
    carThread = _car
    cars_done = 0
    
    v = carThread.m_visitors
    c = carThread.n_cars
    p = carThread.k_pumps
    t = carThread.t_time
    id = carThread.thread_num
    i = 0
    
    while cars_done == 0:
        cars_in_service = 0
        while i < c:
            lock.acquire()
            if cars[i].in_service is True and cars[i].rides < 5:
                # Car still being used, add more time
                cars[i].time += 1
            if cars[i].time == t:
                cars[i].in_service = False
                cars[i].rides += 1
                cars[i].time = 0
            if cars[i].rides == 5 and cars[i].in_queue is False:
                push(i)
                cars[i].in_queue = True
            if cars[i].in_service is False:
                cars_in_service += 1
            i += 1
            lock.release()
        if cars_in_service == c and is_done >= 1:
            cars_done = 1
    lock.acquire()
    is_done += 1
    lock.release()
    
    
    def gas_station_thread(_gas_station):
    global is_done
    gas_station = _gas_station
    
    truck = False
    cars_filled = 0
    
    v = gas_station.m_visitors
    c = gas_station.n_cars
    p = gas_station.k_pumps
    t = gas_station.t_time
    id = gas_station.thread_num
    
    gas_done = 0
    pumps_in_service = 0
    
    j = 0
    while gas_done == 0:
        while j < p:
            lock.acquire()
            if pumps[j].in_use is True:
                pumps[j].time += 1
            if pumps[j].time == 3:
                lock.acquire()
                cars[j].in_service = 0
                cars[j].rides = 0
                cars[j].time = 0
                cars[j].in_queue = False
                lock.release()
                pumps[j].time = 0
                pumps[j].in_use = False
                cars_filled += 1
                pumps_in_service -= 1
            if truck is True and pumps[j].in_use is False:
                truck = False
                print('Fuel Truck is currently filling up the gas station.')
            elif pumps[j].in_use is True and pump_line.size > 0:
                pumps_in_service += 1
                pumps[j].in_use = True
                pumps[j].car_num.pop()
                print('Car %d, pump %d' % (pumps[j].car_num + 1, i + 1))
                pumps[j].time = 0
            j += 1
            lock.release()
    
            if cars_filled > 3:
                print('The Fuel Truck is on its way')
                truck = True
                cars_filled = 0
            if pumps_in_service == 0 and is_done == 2:
                gas_done = True
            lock.acquire()
            is_done += 1
            lock.release()
    
    
    def pop():
    lock.acquire()
    fr = pump_line.front
    f = pump_line.pQueue[fr]
    print("\n%d cars are waiting for pumps" % int(pump_line.size - 1))
    if fr < (pump_line.MAXsize - 1):
        pump_line.front += 1
    else:
        pump_line.front = 0
    lock.release()
    
    return f
    
    
    def push(_c):
    c =_c
    lock.acquire()
    b = pump_line.back
    pump_line.pQueue[b] = c
    print("\n%d cars are waiting for pumps" % int(pump_line.size + 1))
    if b < (pump_line.MAXsize - 1):
        pump_line.back += 1
    else:
        pump_line.back = 0
    lock.release()
    
    
    def isEmpty():
    lock.acquire()
    if (pump_line.front == pump_line.back):
        boolean = True
    else:
        boolean = False
    lock.release()
    
    return boolean
    
    if __name__ == "__main__":
    arguments = Arguments(0, 0, 0, 0, 0, False)
    main = Arguments(0, 0, 0, 0, 0, False)
    thread = []
    round_number = 0
    io_control = 0
    input_file = []
    number_of_threads = 3
    print("Enter the name of the text file to use as input:")
    while io_control == 0:
        try:
            filename = input()
            input_file = open(filename)
            io_control = 1
        except IOError:
            print("Specified file does not exist, enter a different text file:")
    
    # parse file into lines, separating by endlines
    file_lines = []
    num_lines = 0
    for line in input_file:
        line = line.replace(",", " ")
        line = line.split()
        num_lines += 1
        if line:
            line = [int(i) for i in line]
            file_lines.append(line)
    
    while main.done is False and round_number < num_lines:
        main.m_visitors = int(file_lines[round_number][0])
        main.n_cars = int(file_lines[round_number][1])
        main.k_pumps = int(file_lines[round_number][2])
        main.t_time = int(file_lines[round_number][3])
    
        print("\nRound Number: %d" % (round_number + 1))
    
        if main.n_cars == 0:
            print('No data to read in this round, press enter to continue:')
            input()
    
        print("Number of Visitors: %d" % main.m_visitors)
        print("Number of Cars: %d" % main.n_cars)
        print("Number of Pumps: %d" % main.k_pumps)
        print("Units of Time: %d" % main.t_time)
    
        M = main.m_visitors
        N = main.n_cars
        K = main.k_pumps
        T = main.t_time
    
        thread_info = []
        cars = []
        pumps = []
        for i in range(0, 3):
            temp = Arguments(M, N, K, T, i, False)
            thread_info.append(temp)
    
        for i in range(0, N):
            temp = Car(0, 0, 0, False, 0, False)
            cars.append(temp)
    
        for i in range(0, K):
            temp = Pump(0, False, 0, 0)
            pumps.append(temp)
    
        pump_line = PumpQueue(0, 0, 0, 0, N)
    
        visitorThread = Thread(target=visitor_thread, args=(thread_info[0],))
        thread.append(visitorThread)
    
        carsThread = Thread(target=car_thread, args=(thread_info[1],))
        thread.append(carsThread)
    
        gasThread = Thread(target=gas_station_thread, args=(thread_info[2],))
        thread.append(gasThread)
    
        visitorThread.start()
        carsThread.start()
        gasThread.start()
    
        visitorThread.join()
        carsThread.join()
        gasThread.join()
    
        round_number += 1
    

My output:

> Round Number: 1 Number of Visitors: 35 Number of Cars: 6 Number of Pumps: 5
> Units of Time: 2
>
> Visitor 1 is currently in car 1
>
> 34 customers waiting for a ride.
>
> Visitor 2 is currently in car 2
>
> 33 customers waiting for a ride.
>
> Visitor 3 is currently in car 3
>
> 32 customers waiting for a ride.
>
> Visitor 4 is currently in car 4
>
> 31 customers waiting for a ride.
>
> Visitor 5 is currently in car 5
>
> 30 customers waiting for a ride.
>
> Visitor 6 is currently in car 6
>
> 29 customers waiting for a ride.


Answer: I think there's an index problem here:

    
    
     if cars[j].in_service is False and cars[i].rides < 5:
    

should be

    
    
    if cars[j].in_service is False and cars[j].rides < 5:
    

Index for cars is j not i

