Question: I think I'm lacking the understanding on multithreading in Python and the answers online are hurting my feeble brain. I want to use multithreading to write json data to a list as it works. I have the below, the some_function function is a function that takes a dictionary runs a different (basic) web scraper, that returns a dictionary or nothing (it errors). from multiprocessing import Pool def some_function(job_dict: dict) -> dict: # some function class RunJobs: def __init__(self, jobs: list[dict], threads=1): self.jobs, self.threads, self.state = jobs, threads, [] def _run_sequentially(self): for job in self.jobs: self.state.append(some_function(job)) def _run_multithreaded(self): pool = Pool(self.threads) job_queue = [] for job in self.jobs: job_queue.append(pool.apply_async(some_function, args=(job))) pool.close() pool.join() for job in job_queue: self.state.append(job.get()) def run(self): try: if self.threads == 1: self._run_sequentially() else: self._run_multithreaded() except KeyInterrupt: print(self.state)  How do I make it so this state is updated on multiple threads as they run, or is that not possible?

Answer: As been pointed out by Arthur Belanger your code is using multiprocessing and not multithreading. If you want to be using multithreading, then you can do so with minimal changes. Instead of: from multiprocessing import Pool  Do either: from multiprocessing.dummy import Pool # multithreading  or: from multiprocessing.pool import ThreadPool as Pool # multithreading  But you have other issues with your code. In your _run_multithreaded method you have:  job_queue.append(pool.apply_async(self._run_single, args=(job)))  But your class does not have a _run_single method. Looking at your _run_sequentially method it would appear that you mean some_function instead of self._run_single. Also, the args argument to apply_async should be an iterable such as a tuple or list instance and each element will be an positional argument to your worker function, run_job. But enclosing job in parentheses does not make it a tuple; it is just a parenthesized expression which in this case is no different than had you just specified args = job (that is, (job) == job). What you need is:  job_queue.append(pool.apply_async(some_function, args=(job,)))  Note that (job,) is a tuple of one element while (job) is not. You also have quite a few syntax errors. For example: class RunJobs: self __init__(self, jobs: list[dict], threads=1):  That is not how you define a method. This should be: class RunJobs: def __init__(self, jobs: list[dict], threads=1):  Putting it all together, you code should be: from multiprocessing.dummy import Pool def some_function(job_dict: dict) -> dict: # For demo purposes just return the input argument: return job_dict class RunJobs: def __init__(self, jobs: list[dict], threads=1): self.jobs, self.threads, self.state = jobs, threads, [] def _run_sequentially(self): for job in self.jobs: self.state.append(some_function(job)) def _run_multithreaded(self): pool = Pool(self.threads) job_queue = [] for job in self.jobs: job_queue.append(pool.apply_async(some_function, args=(job,))) pool.close() pool.join() for job in job_queue: self.state.append(job.get()) def run(self): try: if self.threads == 1: self._run_sequentially() else: self._run_multithreaded() except KeyInterrupt: print(self.state) if __name__ == '__main__': jobs = [ {'x': 1}, {'y': 2}, {'z': 3} ] run_jobs = RunJobs(jobs, threads=3) run_jobs.run() print(run_jobs.state)  Prints: [{'x': 1}, {'y': 2}, {'z': 3}]  This code should run correctly whether you use your original import statement (multiprocessing) or the above modified one (multithreading). Of course, depending on what some_function is actually doing, one will be more appropriate than the other.