"""
Multiple threads compete for one resource-Protect critical resources-Lock (Lock/RLock)
Multiple threads compete for multiple resources (number of threads> number of resources
)-Semaphore scheduling of multiple threads-Suspend thread execution/ waiting threads wake up - for condition Condition
"""

from concurrent.futures import ThreadPoolExecutor
from random import randint
from time import sleep

import threading

class Account():
  """Bank Account"""

  def __init__(self, balance=0):
    self.balance = balance
    lock = threading.Lock()
    self.condition = threading.Condition(lock)

  def withdraw(self, money):
    with self.condition:
      while money > self.balance:
        self.condition.wait()
      new_balance = self.balance - money
      sleep(0.0001)
      self.balance = new_balance

  def deposit(self, money):
    with self.condition:
      new_balance = self.balance + money
      sleep(0.0001)
      self.balance = new_balance
      self.condition.notify_all()

def add_money(account):
  while True:
    money = randint(10, 30)
    account.deposit(money)
    print(threading.current_thread().name, ':', money, '======> ', account.balance)
    sleep(0.5)

def sub_money(account):
  while True:
    money = randint(10, 30)
    account.withdraw(money)
    print(threading.current_thread().name,':', money, '<====', account.balance)
    sleep(1)

def main():
  account = Account()
  with ThreadPoolExecutor(max_workers=10) as pool:
    for _ in range(5):
      pool.submit(add_money, account)
      pool.submit(sub_money, account)

if __name__ == '__main__':
    main()
