<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://entorb.net//wiki/index.php?action=history&amp;feed=atom&amp;title=Python_-_Multithreading</id>
	<title>Python - Multithreading - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://entorb.net//wiki/index.php?action=history&amp;feed=atom&amp;title=Python_-_Multithreading"/>
	<link rel="alternate" type="text/html" href="https://entorb.net//wiki/index.php?title=Python_-_Multithreading&amp;action=history"/>
	<updated>2026-05-06T10:27:06Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://entorb.net//wiki/index.php?title=Python_-_Multithreading&amp;diff=4843&amp;oldid=prev</id>
		<title>Torben at 20:21, 30 October 2024</title>
		<link rel="alternate" type="text/html" href="https://entorb.net//wiki/index.php?title=Python_-_Multithreading&amp;diff=4843&amp;oldid=prev"/>
		<updated>2024-10-30T20:21:29Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[Category:Coding]][[Category:Python]]&lt;br /&gt;
===When to use which===&lt;br /&gt;
see [https://timber.io/blog/multiprocessing-vs-multithreading-in-python-what-you-need-to-know/] &amp;lt;br/&amp;gt;&lt;br /&gt;
use threading if your program is I/O/network limited &amp;lt;br/&amp;gt;&lt;br /&gt;
use multiprocessing if it&amp;#039;s CPU limited. (here the entire memory is copied into each subprocess)&lt;br /&gt;
&lt;br /&gt;
====Example for both====&lt;br /&gt;
 import multiprocessing&lt;br /&gt;
 import threading&lt;br /&gt;
 import queue&lt;br /&gt;
 import os&lt;br /&gt;
 import time&lt;br /&gt;
 import math&lt;br /&gt;
 &lt;br /&gt;
 def worker_core(i: int, s: str) -&amp;gt; list:&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     used in worker_core_cpu and worker_core_io&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     result = i, s, os.getpid()&lt;br /&gt;
     time.sleep(.1)&lt;br /&gt;
     return result&lt;br /&gt;
 &lt;br /&gt;
 def worker_core_cpu(i: int, s: str) -&amp;gt; list:&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     used in all CPU limited worker implementations&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     sum = 0&lt;br /&gt;
     for j in range(1_000):&lt;br /&gt;
         sum += math.sqrt(i+j)&lt;br /&gt;
     result = worker_core(i=i, s=s)&lt;br /&gt;
     return result&lt;br /&gt;
 &lt;br /&gt;
 def worker_core_io(i: int, s: str) -&amp;gt; list:&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     used in all I/O limited worker implementations&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     time.sleep(.1)&lt;br /&gt;
     result = worker_core(i=i, s=s)&lt;br /&gt;
     return result&lt;br /&gt;
 &lt;br /&gt;
 def worker_multiprocessing_1(i: int, s: str) -&amp;gt; list:&lt;br /&gt;
     &amp;quot; used in multiprocessing_1 &amp;quot;&lt;br /&gt;
     return worker_core_cpu(i=i, s=s)&lt;br /&gt;
 &lt;br /&gt;
 def worker_multiprocessing_2(q_work: multiprocessing.Queue, q_results: multiprocessing.Queue):&lt;br /&gt;
     &amp;quot; used in multiprocessing_2 -&amp;gt; not faster than multiprocessing_1&amp;quot;&lt;br /&gt;
     while not q_work.empty():&lt;br /&gt;
         i, s = q_work.get()&lt;br /&gt;
         result = worker_core_cpu(i=i, s=s)&lt;br /&gt;
         q_results.put(result)&lt;br /&gt;
 &lt;br /&gt;
 def worker_threading_1(q_work: queue.Queue, results: dict):&lt;br /&gt;
     &amp;quot;used in threading_1&amp;quot;&lt;br /&gt;
     while not q_work.empty():&lt;br /&gt;
         i, s = q_work.get()&lt;br /&gt;
         result = worker_core_io(i=i, s=s)&lt;br /&gt;
         results[i] = result&lt;br /&gt;
         q_work.task_done()&lt;br /&gt;
 &lt;br /&gt;
 def multiprocessing_1(l_pile_of_work: list):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     for CPU limited tasks&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     num_processes = min(multiprocessing.cpu_count()-1, len(l_pile_of_work))&lt;br /&gt;
     pool = multiprocessing.Pool(processes=num_processes)&lt;br /&gt;
     # use map for 1 argument, use starmap for multiple arguments for worker function&lt;br /&gt;
     # here each item in l_pile_of_work is a list of 3 elements -&amp;gt; starmap&lt;br /&gt;
     l_results_unsorted = pool.starmap(&lt;br /&gt;
         func=worker_multiprocessing_1,&lt;br /&gt;
         iterable=l_pile_of_work)&lt;br /&gt;
     l_results = sorted(l_results_unsorted)  # sort by id&lt;br /&gt;
     return l_results&lt;br /&gt;
 &lt;br /&gt;
 def multiprocessing_2(l_pile_of_work: list):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     for CPU limited tasks&lt;br /&gt;
     not using not re-creation of processes, but fixed number instead&lt;br /&gt;
     result: slower than multiprocessing_1&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     q_pile_of_work = multiprocessing.Queue(maxsize=len(l_pile_of_work))&lt;br /&gt;
     q_results = multiprocessing.Queue(maxsize=len(l_pile_of_work))&lt;br /&gt;
 &lt;br /&gt;
     for params in l_pile_of_work:&lt;br /&gt;
         q_pile_of_work.put(params)&lt;br /&gt;
     del params&lt;br /&gt;
 &lt;br /&gt;
     num_processes = min(multiprocessing.cpu_count()-1, len(l_pile_of_work))&lt;br /&gt;
     l_processes = []&lt;br /&gt;
     for i in range(num_processes):&lt;br /&gt;
         p = multiprocessing.Process(&lt;br /&gt;
             name=&amp;#039;myProcess-&amp;#039;+str(i),&lt;br /&gt;
             target=worker_multiprocessing_2,&lt;br /&gt;
             args=(q_pile_of_work, q_results),&lt;br /&gt;
             daemon=True)&lt;br /&gt;
         l_processes.append(p)&lt;br /&gt;
     del i&lt;br /&gt;
     for p in l_processes:&lt;br /&gt;
         p.start()&lt;br /&gt;
     print(&amp;quot;started&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
     while not q_results.full():&lt;br /&gt;
         time.sleep(.1)&lt;br /&gt;
     for p in l_processes:&lt;br /&gt;
         p.terminate()&lt;br /&gt;
         p.join()&lt;br /&gt;
         p.close()&lt;br /&gt;
     print(&amp;quot;closed&amp;quot;)&lt;br /&gt;
     del p&lt;br /&gt;
 &lt;br /&gt;
     l_results_unsorted = []&lt;br /&gt;
     while not q_results.empty():&lt;br /&gt;
         result = q_results.get()&lt;br /&gt;
         l_results_unsorted.append(result)&lt;br /&gt;
     l_results = sorted(l_results_unsorted)  # sort by id&lt;br /&gt;
     return l_results&lt;br /&gt;
 &lt;br /&gt;
 def threading_1(l_pile_of_work: list, num_threads: int):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     for I/O limited tasks&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     q_pile_of_work = queue.Queue(&lt;br /&gt;
         maxsize=len(l_pile_of_work))  # maxsize=0 -&amp;gt; unlimited&lt;br /&gt;
     for params in l_pile_of_work:&lt;br /&gt;
         q_pile_of_work.put(params)&lt;br /&gt;
     d_results = {}  # threads can write into dict&lt;br /&gt;
 &lt;br /&gt;
     l_threads = []  # List of threads, not used here&lt;br /&gt;
     for i in range(num_threads):&lt;br /&gt;
         t = threading.Thread(name=&amp;#039;myThread-&amp;#039;+str(i),&lt;br /&gt;
                              target=worker_threading_1,&lt;br /&gt;
                              args=(q_pile_of_work, d_results),&lt;br /&gt;
                              daemon=True)&lt;br /&gt;
         l_threads.append(t)&lt;br /&gt;
         t.start()&lt;br /&gt;
     q_pile_of_work.join()  # wait for all threas to complete&lt;br /&gt;
     l_results_unsorted = d_results.values()&lt;br /&gt;
     l_results = sorted(l_results_unsorted)  # sort by id&lt;br /&gt;
     return l_results&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
     l_pile_of_work = []&lt;br /&gt;
     loops = 1_000&lt;br /&gt;
     for i in range(loops):&lt;br /&gt;
         # use index as first parameter&lt;br /&gt;
         L2 = (i, &amp;quot;n&amp;quot;+str(i))&lt;br /&gt;
         l_pile_of_work.append((L2))&lt;br /&gt;
     del L2, i&lt;br /&gt;
     time_start = time.time()&lt;br /&gt;
     # results = multiprocessing_1(l_pile_of_work)&lt;br /&gt;
     # # or&lt;br /&gt;
     # results = multiprocessing_2(l_pile_of_work)&lt;br /&gt;
     # # or&lt;br /&gt;
     results = threading_1(l_pile_of_work, num_threads=100)&lt;br /&gt;
     duration = time.time() - time_start&lt;br /&gt;
     print(&amp;quot;%d sec = %.1f min&amp;quot; % (duration, duration/60))&lt;br /&gt;
 &lt;br /&gt;
     # for res in results:&lt;br /&gt;
     #     print(&amp;#039;task %d, name %s was done in process %d&amp;#039; % (res))&lt;br /&gt;
     # print(len(results))&lt;br /&gt;
&lt;br /&gt;
===Threading V2: 18.04.2020===&lt;br /&gt;
see [https://docs.python.org/3/library/concurrent.futures.html]&lt;br /&gt;
 import multiprocessing as mp  # for fetching number of CPUs&lt;br /&gt;
 import threading&lt;br /&gt;
 import concurrent.futures&lt;br /&gt;
 &lt;br /&gt;
 def series_of_fits_multi_threading(data: list, fit_range: int = 7, max_days_past=14) -&amp;gt; list:&lt;br /&gt;
    fit_series_res = {}&lt;br /&gt;
    l_last_days_for_fit = range(0, -max_days_past, -1)&lt;br /&gt;
    with concurrent.futures.ThreadPoolExecutor(max_workers=mp.cpu_count()) as executor:&lt;br /&gt;
        # Start the load operations and mark each future with its data set&lt;br /&gt;
        list_future = {executor.submit(&lt;br /&gt;
            series_of_fits_worker_thread, data, fit_range, last_day_for_fit): last_day_for_fit for last_day_for_fit in l_last_days_for_fit}&lt;br /&gt;
        for future in concurrent.futures.as_completed(list_future):&lt;br /&gt;
            last_day_for_fit = list_future[future]&lt;br /&gt;
            d_this_fit_result = {}&lt;br /&gt;
            try:&lt;br /&gt;
                d_this_fit_result = future.result()&lt;br /&gt;
            except Exception as exc:&lt;br /&gt;
                print(&amp;#039;%r generated an exception: %s&amp;#039; % (day, exc))&lt;br /&gt;
            if len(d_this_fit_result) != 0:&lt;br /&gt;
                fit_series_res[last_day_for_fit] = round(&lt;br /&gt;
                    d_this_fit_result[&amp;#039;fit_res&amp;#039;][1], 1)&lt;br /&gt;
    return fit_series_res&lt;br /&gt;
 &lt;br /&gt;
 def series_of_fits_worker_thread(data: list, fit_range: int, last_day_for_fit: int):&lt;br /&gt;
    # print(threading.currentThread().getName(), &amp;#039;Starting&amp;#039;)&lt;br /&gt;
    d = fit_routine(&lt;br /&gt;
        data, (last_day_for_fit-fit_range, last_day_for_fit))&lt;br /&gt;
    return d&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====V1====&lt;br /&gt;
 import threading&lt;br /&gt;
 import logging&lt;br /&gt;
 import time&lt;br /&gt;
 import multiprocessing as mp # for fetching number of CPUs&lt;br /&gt;
 &lt;br /&gt;
 # Python’s built-in data structures (lists, dictionaries, etc.) are thread-safe&lt;br /&gt;
 #  as a side-effect of having atomic byte-codes for manipulating them&lt;br /&gt;
 # -&amp;gt; so locking is them is not required.&lt;br /&gt;
 # Other data structures implemented in Python, or simpler types like integers and floats, don’t have that protection.&lt;br /&gt;
 # from inside a thread it is possible to manipulate dic but not integers&lt;br /&gt;
 &lt;br /&gt;
 # Logging is nicer than print, as it can automatically add the threadname&lt;br /&gt;
 logging.basicConfig(level=logging.DEBUG,&lt;br /&gt;
                     format=&amp;#039;%(asctime)s %(levelname)s %(threadName)s: %(message)s&amp;#039;,&lt;br /&gt;
                     )&lt;br /&gt;
 &lt;br /&gt;
 # List of &amp;quot;work&amp;quot; to to&lt;br /&gt;
 listPileOfWork = [x for x in range(7)]&lt;br /&gt;
 &lt;br /&gt;
 # Threads can not write in integers, but in dictionaries !&lt;br /&gt;
 # counter = 0&lt;br /&gt;
 dic = {}&lt;br /&gt;
 dic[&amp;quot;Counter&amp;quot;] = 0&lt;br /&gt;
 &lt;br /&gt;
 def worker():&lt;br /&gt;
     logging.info(&amp;#039;Starting&amp;#039;)&lt;br /&gt;
     # print(threading.currentThread().getName(), &amp;#039;Starting&amp;#039;)&lt;br /&gt;
     while listPileOfWork :&lt;br /&gt;
         x = listPileOfWork.pop(0)&lt;br /&gt;
         logging.info(&amp;quot;Took&amp;quot; + str(x))&lt;br /&gt;
         dic[&amp;quot;Counter&amp;quot;] += x&lt;br /&gt;
         time.sleep(2)&lt;br /&gt;
     logging.info(&amp;#039;No more work to do&amp;#039;)&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 threads = []  # List of threads&lt;br /&gt;
 &lt;br /&gt;
 for i in range(mp.cpu_count()):&lt;br /&gt;
     t = threading.Thread(name=&amp;#039;myThread&amp;#039;+str(i), target=worker)&lt;br /&gt;
     threads.append(t)&lt;br /&gt;
     t.start()&lt;br /&gt;
 &lt;br /&gt;
 # Wait for all threads to complete&lt;br /&gt;
 for t in threads:&lt;br /&gt;
     t.join()&lt;br /&gt;
 logging.info(&amp;#039;All threads done&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
===Multiprocessing===&lt;br /&gt;
see [https://zetcode.com/python/multiprocessing/]&lt;br /&gt;
====V2====&lt;br /&gt;
 import multiprocessing&lt;br /&gt;
 from os import getpid&lt;br /&gt;
 import time&lt;br /&gt;
 &lt;br /&gt;
 def worker(i, s):&lt;br /&gt;
     print(i)&lt;br /&gt;
     #     print(&amp;#039;I am number %d, %s in process %d&amp;#039; % (i, str, getpid()))&lt;br /&gt;
     time.sleep(.1)&lt;br /&gt;
     return i, s, getpid()&lt;br /&gt;
 &lt;br /&gt;
 if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
     pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())&lt;br /&gt;
     L1 = []&lt;br /&gt;
     for i in range(100):&lt;br /&gt;
         L2 = (i, &amp;quot;n&amp;quot;+str(i))&lt;br /&gt;
         L1.append(L2)&lt;br /&gt;
     # map for 1 argument, starmap for multiple arguments at worker&lt;br /&gt;
     results_unsorted = pool.starmap(worker, L1)&lt;br /&gt;
     results = sorted(results_unsorted)&lt;br /&gt;
     for res in results:&lt;br /&gt;
         print(&amp;#039;task %d, name %s was done in process %d&amp;#039; % (res))&lt;br /&gt;
&lt;br /&gt;
====V1====&lt;br /&gt;
From [https://stackoverflow.com/questions/3044580/multiprocessing-vs-threading-python]: The threading module uses threads, the multiprocessing module uses processes. The difference is that threads run in the same memory space, while processes have separate memory. This makes it a bit harder to share objects between processes with multiprocessing. Since threads use the same memory, precautions have to be taken or two threads will write to the same memory at the same time. This is what the global interpreter lock is for. Spawning processes is a bit slower than spawning threads. Once they are running, there is not much difference.&lt;br /&gt;
&lt;br /&gt;
Example from [http://www.cs.sfu.ca/CC/383/ggbaker/examples/parallel/]&lt;br /&gt;
&lt;br /&gt;
Prob: KeyboardInterrupt (CTRL+C) does not close program completely see [http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/] as starting point for a solution&lt;br /&gt;
&lt;br /&gt;
 from multiprocessing import Pool&lt;br /&gt;
 &lt;br /&gt;
 def escapes(cr, ci, it):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;Does iterating z &amp;lt;- z^2 + c escape after it iterations?&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     zr = 0.0&lt;br /&gt;
     zi = 0.0&lt;br /&gt;
     for i in xrange(it):&lt;br /&gt;
         # z &amp;lt;- z^2 + c&lt;br /&gt;
         zr,zi = zr*zr - zi*zi + cr, 2*zr*zi + ci&lt;br /&gt;
         if zr*zr + zi*zi &amp;gt; 4:&lt;br /&gt;
             return True&lt;br /&gt;
     return False&lt;br /&gt;
 &lt;br /&gt;
 def toChar(p):&lt;br /&gt;
     if p: return &amp;quot; &amp;quot;&lt;br /&gt;
     else: return &amp;quot;X&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 def doRow((xmin,xmax,xstep, ymin,ymax,ystep, iterations, yc)):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;Calculate one row of the output.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     y = yc*(ymax-ymin)/ystep + ymin&lt;br /&gt;
     row = []&lt;br /&gt;
     for xc in xrange(xstep):&lt;br /&gt;
         x = xc*(xmax-xmin)/xstep + xmin&lt;br /&gt;
         row.append( escapes(x, y, iterations) )&lt;br /&gt;
     return &amp;quot;&amp;quot;.join([toChar(p) for p in row])&lt;br /&gt;
 &lt;br /&gt;
 def mandel(xmin,xmax,xstep, ymin,ymax,ystep, iterations):&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;Calculate and print a Mandelbrot set.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
     pool = Pool()  # askes the os for num of cpus ;-)&lt;br /&gt;
     results = []&lt;br /&gt;
     for yc in xrange(ystep):&lt;br /&gt;
         res = pool.apply_async(doRow, ((xmin,xmax,xstep, ymin,ymax,ystep, iterations, yc),))&lt;br /&gt;
         results.append(res)&lt;br /&gt;
     for yc in xrange(ystep):&lt;br /&gt;
         print results[yc].get()&lt;br /&gt;
 &lt;br /&gt;
 if __name__==&amp;quot;__main__&amp;quot;:&lt;br /&gt;
     mandel(-2.0, 1.0, 80, -1.0, 1.0, 24, 20000)&lt;/div&gt;</summary>
		<author><name>Torben</name></author>
	</entry>
</feed>