{"id":9776,"date":"2016-01-05T16:11:03","date_gmt":"2016-01-05T14:11:03","guid":{"rendered":"http:\/\/www.webcodegeeks.com\/?p=9776"},"modified":"2018-01-09T09:50:33","modified_gmt":"2018-01-09T07:50:33","slug":"python-threading-concurrency-example","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/","title":{"rendered":"Python Threading \/ Concurrency Example"},"content":{"rendered":"<p>Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in parallel so it would take half the time.<\/p>\n<p>This is not true in most cases. Using CPython, there is a mutex that prevents multiple native threads from executing Python byte codes at once. It&#8217;s called GIL (global interpreter lock). This lock is necessary mainly because CPython&#8217;s memory management is not thread-safe, but notice that I\/O, image processing, and other potentially blocking operations, happen outside the GIL, so it will only become a bottle neck in processes that spend a lot of time inside the GIL.<\/p>\n<p>In most applications nowadays, concurrency is something we all must be able to handle. Mostly in web applications, where one request usually starts a thread, we need to have concurrency and threading in mind so we can write our programs accordingly.<\/p>\n<p>Threading is also a good solution to optimize response times. Given a scenario in which we have to process 4 million objects, a good thing to do would be divide them in 4 groups of a million objects and process them in 4 separated threads.<br \/>\n&nbsp;<br \/>\n[ulp id=&#8217;R7QVpFMZmjosABLC&#8217;]<\/p>\n<h2>1. Python <em>_thread<\/em> module<\/h2>\n<p>The <code>_thread<\/code> module is very effective for low level threading, let&#8217;s see an example to understand the concept. But keep in mind that since Python 2.4, this module is not used anymore.<\/p>\n<p>The process of spawning a thread is pretty simple. We just need to call a method called <code>start_new_thread<\/code>, available in the <code>_thread<\/code> module, which receives a function, and arguments to pass to it. It returns immediately and a thread will run in parallel. Let&#8217;s see:<\/p>\n<pre class=\"brush:python; wrap-lines:false\">\r\nimport _thread as thread\r\nimport time\r\n\r\nexecuted_count = 0\r\n\r\n\r\n# Define a function for the thread\r\ndef print_time(thread_name, delay):\r\n    global executed_count\r\n    count = 0\r\n    while count &lt; 5:\r\n        time.sleep(delay)\r\n        count += 1\r\n        print(&quot;%s: %s&quot; % (thread_name, time.ctime(time.time())))\r\n    executed_count += 1\r\n\r\n\r\n# Create two threads as follows\r\ntry:\r\n    threads = [thread.start_new_thread(print_time, (&quot;Thread-1&quot;, 2,)),\r\n               thread.start_new_thread(print_time, (&quot;Thread-2&quot;, 4,))]\r\nexcept:\r\n    print(&quot;Error: unable to start thread&quot;)\r\n\r\nwhile executed_count &lt; 2:\r\n    pass\r\n<\/pre>\n<p>When we run this script we&#8217;ll see an output that looks like:<\/p>\n<pre class=\"brush:bash; wrap-lines:false\">\r\nThread-1: Mon Dec 21 12:55:23 2015\r\nThread-2: Mon Dec 21 12:55:25 2015\r\nThread-1: Mon Dec 21 12:55:25 2015\r\nThread-1: Mon Dec 21 12:55:27 2015\r\nThread-2: Mon Dec 21 12:55:29 2015\r\nThread-1: Mon Dec 21 12:55:29 2015\r\nThread-1: Mon Dec 21 12:55:31 2015\r\nThread-2: Mon Dec 21 12:55:33 2015\r\nThread-2: Mon Dec 21 12:55:37 2015\r\nThread-2: Mon Dec 21 12:55:41 2015\r\n<\/pre>\n<p>So, let&#8217;s see what is going on there.<\/p>\n<p>We have a variable called <code>executed_count<\/code> initialized in zero. Then there is a function called <code>print_time<\/code> which receives the name of the thread and a delay, and every {delay} seconds it will print the date with the thread name, five times. Then it adds one to the <code>executed_count<\/code> variable.<\/p>\n<p>Then we create two threads, each of them containing our <code>print_time<\/code> function, with a name and a delay assigned to them. And we have a while which makes sure the program won&#8217;t exit until <code>executed_count<\/code> is equal or greater than 2.<\/p>\n<h2>2. Python threading module<\/h2>\n<p>The newer <code>threading<\/code> module included with Python 2.4 provides much more powerful, high-level support for threads than the <code>_thread<\/code> module.<\/p>\n<h3>2.1. Extending Thread<\/h3>\n<p>The most used procedure for spawning a thread using this module, is defining a subclass of the <code>Thread<\/code> class. Once you&#8217;ve done it, you should override the <code>__init__<\/code> and <code>run<\/code> methods.<\/p>\n<p>Once you&#8217;ve got your class, you just instantiate an object of it and call the method called <code>start<\/code>. Let&#8217;s see an example of this:<\/p>\n<pre class=\"brush:python; wrap-lines:false\">\r\nimport threading\r\n\r\nimport time\r\n\r\n\r\nclass MyThread(threading.Thread):\r\n    def __init__(self, name, sleep_time):\r\n        threading.Thread.__init__(self)\r\n        self.name = name\r\n        self.sleep_time = sleep_time\r\n\r\n    def run(self):\r\n        print(\"{} start\".format(self.name))\r\n        time.sleep(self.sleep_time)\r\n        print(\"{} end\".format(self.name))\r\n\r\n\r\nthreads = [MyThread(\"Thread-{}\".format(i), i) for i in range(1, 4)]\r\nfor t in threads:\r\n    t.start()\r\n<\/pre>\n<p>Then we run it an see something like:<\/p>\n<pre class=\"brush:bash; wrap-lines: false\">\r\nThread-1 start\r\nThread-2 start\r\nThread-3 start\r\nThread-1 end\r\nThread-2 end\r\nThread-3 end\r\n<\/pre>\n<p>Of course, we don&#8217;t need to name our threads like that. Each Thread instance has a name with a default value that can be changed as the thread is created. Naming threads is useful in server processes with multiple service threads handling different operations. So, let&#8217;s change our code to avoid reinventing the wheel. In our constructor:<\/p>\n<pre class=\"brush:python; wrap-lines:false\">\r\ndef __init__(self, sleep_time):\r\n    threading.Thread.__init__(self)\r\n    threading.Thread.__init__(self)\r\n    self.sleep_time = sleep_time\r\n<\/pre>\n<p>Then the output will look like:<\/p>\n<pre class=\"brush:bash\">\r\nThread-2 start\r\nThread-4 start\r\nThread-6 start\r\nThread-2 end\r\nThread-4 end\r\nThread-6 end\r\n<\/pre>\n<p>But it usually isn&#8217;t so simple, the logic isn&#8217;t in the run method ouf our <code>Thread<\/code> class, so let&#8217;s make it a little more real and do the <code>print<\/code> and sleep in a method outside our <code>MyThread<\/code>.<\/p>\n<h3>2.2. Getting Current Thread Information<\/h3>\n<p>Our problem here is that we don&#8217;t have our thread name outside of it, and we don&#8217;t want that information going around in our function&#8217;s signature. But the <code>threading<\/code> module, has some methods that give us access to the current thread information:<\/p>\n<pre class=\"brush:python\">\r\nimport threading\r\nimport time\r\n\r\n\r\ndef my_logic(sleep_time):\r\n    thread_name = threading.current_thread().getName()\r\n    print(\"{} start\".format(thread_name))\r\n    time.sleep(sleep_time)\r\n    print(\"{} end\".format(thread_name))\r\n\r\n\r\nclass MyThread(threading.Thread):\r\n    def __init__(self, sleep_time):\r\n        threading.Thread.__init__(self)\r\n        threading.Thread.__init__(self)\r\n        self.sleep_time = sleep_time\r\n\r\n    def run(self):\r\n        my_logic(self.sleep_time)\r\n\r\n\r\nthreads = [MyThread(i) for i in range(1, 4)]\r\nfor t in threads:\r\n    t.start()\r\n\r\n<\/pre>\n<p>By executing <code>threading.current_thread()<\/code>, we gain access to the current thread information. Among that information we can find its status (<code>is_alive()<\/code>), its daemon flag (<code>isDaemon()<\/code>), and other useful methods.<\/p>\n<h3>2.3. Daemon Threads<\/h3>\n<p>Now, let&#8217;s talk about daemon threads. Until now, our programs waited for every thread to end before actually terminating, but sometimes we don&#8217;t want that behaviour. If we have a thread, pushing status or metrics to a service, we usually don&#8217;t care if it has finished or not when we shut down our program, and maybe we don&#8217;t want to explicitly terminate it before exiting.<\/p>\n<p>Daemon threads run without blocking the main thread from exiting. They are useful when we have services where there may not be an easy way to interrupt the thread or where letting the thread die in the middle of its work does not lose or corrupt data.<\/p>\n<p>To spawn a daemon thread, we just spawn a normal thread an call its <code>setDaemon()<\/code> method with <code>True<\/code> as a parameter. By default threads are not daemon. Let&#8217;s se how our program behaves when we make one of those threads daemon:<\/p>\n<pre class=\"brush:python; wrap-lines: false\">\r\nthreads = [MyThread(i) for i in range(1, 4)]\r\nthreads[2].setDaemon(True)\r\nfor t in threads:\r\n    t.start()\r\n<\/pre>\n<p>We are now grabbing the last thread we create, and making it daemon. The output will now look like:<\/p>\n<pre class=\"brush:bash; wrap-lines: false\">\r\nThread-2 start\r\nThread-4 start\r\nThread-6 start\r\nThread-2 end\r\nThread-4 end\r\n<\/pre>\n<p>As you can see, the main thread is not waiting for Thread-6 to finish before exiting, daemon threads are terminated when the main thread finished its execution.<\/p>\n<p>Let&#8217;s write something that resembles a real-life problem solution. Let&#8217;s make a script, that given an array of URL&#8217;s, crawls them and saves the html in files.<\/p>\n<p><span style=\"text-decoration: underline\"><em>site-crawler.py<\/em><\/span><\/p>\n<pre class=\"brush:python; wrap-lines:true\">\r\nimport http.client\r\nimport threading\r\nimport logging\r\n\r\nlogging.basicConfig(level=logging.INFO, format='(%(threadName)-10s) %(message)s', )\r\n\r\n\r\ndef save(html, file_absolute_path):\r\n    logging.info(\"saving {} bytes to {}\".format(len(html), file_absolute_path))\r\n    with open(file_absolute_path, 'wb+') as file:\r\n        file.write(html)\r\n        file.flush()\r\n\r\n\r\ndef crawl(req):\r\n    logging.info(\"executing get request for parameters: {}\".format(str(req)))\r\n    connection = http.client.HTTPConnection(req[\"host\"], req[\"port\"])\r\n    connection.request(\"GET\", req[\"path\"])\r\n    response = connection.getresponse()\r\n    logging.info(\"got {} response http code\".format(response.status))\r\n    logging.debug(\"headers: {}\".format(str(response.headers)))\r\n    response_content = response.read()\r\n    logging.debug(\"actual response: {}\".format(response_content))\r\n    return response_content\r\n\r\n\r\nclass MyCrawler(threading.Thread):\r\n    def __init__(self, req, file_path):\r\n        threading.Thread.__init__(self, name=\"Crawler-{}\".format(req[\"host\"]))\r\n        self.req = req\r\n        self.file_path = file_path\r\n\r\n    def run(self):\r\n        global executed_crawlers\r\n        html = crawl(self.req)\r\n        save(html, self.file_path)\r\n\r\n\r\ndef __main__():\r\n    continue_input = True\r\n    threads = []\r\n    while continue_input:\r\n        host = input(\"host: \")\r\n        port = 80  # int(input(\"port: \"))\r\n        path = \"\/\"  # input(\"path: \")\r\n        file_path = input(\"output file absolute path: \")\r\n        req = {\"host\": host, \"port\": port, \"path\": path}\r\n        threads.append(MyCrawler(req, file_path))\r\n        continue_input = input(\"add another? (y\/N) \") == \"y\"\r\n\r\n    for t in threads:\r\n        t.start()\r\n        # t.join()\r\n\r\n__main__()\r\n\r\n\r\n<\/pre>\n<p>So, what do we&#8217;ve got here? This is a script that asks the user to input a host and the absolute path of the file to which it&#8217;ll write the site&#8217;s output html, and measures the time it&#8217;ll take. The script as is gives the following input:<\/p>\n<pre class=\"brush:bash\">\r\nhost: www.google.com.ar\r\noutput file absolute path: \/tmp\/google-ar.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.br\r\noutput file absolute path: \/tmp\/google-br.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.co\r\noutput file absolute path: \/tmp\/google-co.html\r\nadd another? (y\/N) y\r\nhost: www.google.cl\r\noutput file absolute path: \/tmp\/google-cl.html\r\nadd another? (y\/N)\r\n(Crawler-www.google.com.ar) executing get request for parameters: {'path': '\/', 'host': 'www.google.com.ar', 'port': 80}\r\n(Crawler-www.google.com.br) executing get request for parameters: {'path': '\/', 'host': 'www.google.com.br', 'port': 80}\r\n(Crawler-www.google.com.co) executing get request for parameters: {'path': '\/', 'host': 'www.google.com.co', 'port': 80}\r\n(Crawler-www.google.cl) executing get request for parameters: {'path': '\/', 'host': 'www.google.cl', 'port': 80}\r\n(Crawler-www.google.com.co) got 200 response http code\r\n(Crawler-www.google.com.ar) got 200 response http code\r\n(Crawler-www.google.com.br) got 200 response http code\r\n(Crawler-www.google.com.co) saving 53181 bytes to \/tmp\/google-co.html\r\n(Crawler-www.google.com.ar) saving 53008 bytes to \/tmp\/google-ar.html\r\n(Crawler-www.google.com.br) saving 53605 bytes to \/tmp\/google-br.html\r\n(Crawler-www.google.cl) got 200 response http code\r\n(Crawler-www.google.cl) saving 53069 bytes to \/tmp\/google-cl.html\r\n<\/pre>\n<p>As the log shows us, threads are running in parallel, a thread runs while the others make I\/O operations (write to a file, or connect via http to a server). Now, there is a line commented on the <code>__main__()<\/code> function that says <code>t.join()<\/code>, the join method, causes the <em>current thread<\/em> to wait for the thread it joined before continuing its execution, and the log looks like:<\/p>\n<pre class=\"brush:bash\">\r\nhost: www.google.com.ar\r\noutput file absolute path: \/tmp\/google-ar.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.br\r\noutput file absolute path: \/tmp\/google-ar.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.co\r\noutput file absolute path: \/tmp\/google-co.html\r\nadd another? (y\/N) y\r\nhost: www.google.cl\r\noutput file absolute path: \/tmp\/google-cl.html\r\nadd another? (y\/N)\r\n(Crawler-www.google.com.ar) executing get request for parameters: {'port': 80, 'path': '\/', 'host': 'www.google.com.ar'}\r\n(Crawler-www.google.com.ar) got 200 response http code\r\n(Crawler-www.google.com.ar) saving 52973 bytes to \/tmp\/google-ar.html\r\n(Crawler-www.google.com.br) executing get request for parameters: {'port': 80, 'path': '\/', 'host': 'www.google.com.br'}\r\n(Crawler-www.google.com.br) got 200 response http code\r\n(Crawler-www.google.com.br) saving 54991 bytes to \/tmp\/google-ar.html\r\n(Crawler-www.google.com.co) executing get request for parameters: {'port': 80, 'path': '\/', 'host': 'www.google.com.co'}\r\n(Crawler-www.google.com.co) got 200 response http code\r\n(Crawler-www.google.com.co) saving 53172 bytes to \/tmp\/google-co.html\r\n(Crawler-www.google.cl) executing get request for parameters: {'port': 80, 'path': '\/', 'host': 'www.google.cl'}\r\n(Crawler-www.google.cl) got 200 response http code\r\n(Crawler-www.google.cl) saving 53110 bytes to \/tmp\/google-cl.html\r\n<\/pre>\n<p>See? First it crawls google Argentina, then Brazil, and so on. You sure are wondering why would someone do this. Well&#8230; this is not the only use case of the <code>join<\/code> method. Imagine these threads where <em>daemon<\/em>, and you don&#8217;t have control over that. You would have to instantiate a variable which holds the amount of threads executed and then wait for it to equal the number of threads that must be executed before exiting the main thread. It&#8217;s not very elegant.<\/p>\n<h3>2.4. Joining Threads<\/h3>\n<p>Well, there is another way, let&#8217;s make these threads daemon, just to experiment a little bit, and wait for all of them to finish before exiting the main thread:<\/p>\n<p><span style=\"text-decoration: underline\"><em>site-crawler.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nimport http.client\r\nimport threading\r\nimport logging\r\n\r\nlogging.basicConfig(level=logging.INFO, format='(%(threadName)-10s) %(message)s', )\r\n\r\n\r\ndef save(html, file_absolute_path):\r\n    logging.info(\"saving {} bytes to {}\".format(len(html), file_absolute_path))\r\n    with open(file_absolute_path, 'wb+') as file:\r\n        file.write(html)\r\n        file.flush()\r\n\r\n\r\ndef crawl(req):\r\n    logging.info(\"executing get request for parameters: {}\".format(str(req)))\r\n    connection = http.client.HTTPConnection(req[\"host\"], req[\"port\"])\r\n    connection.request(\"GET\", req[\"path\"])\r\n    response = connection.getresponse()\r\n    logging.info(\"got {} response http code\".format(response.status))\r\n    logging.debug(\"headers: {}\".format(str(response.headers)))\r\n    response_content = response.read()\r\n    logging.debug(\"actual response: {}\".format(response_content))\r\n    return response_content\r\n\r\n\r\nclass MyCrawler(threading.Thread):\r\n    def __init__(self, req, file_path):\r\n        threading.Thread.__init__(self, name=\"Crawler-{}\".format(req[\"host\"]), daemon=True)\r\n        self.req = req\r\n        self.file_path = file_path\r\n\r\n    def run(self):\r\n        global executed_crawlers\r\n        html = crawl(self.req)\r\n        save(html, self.file_path)\r\n\r\n\r\ndef __main__():\r\n    continue_input = True\r\n    threads = []\r\n    while continue_input:\r\n        host = input(\"host: \")\r\n        port = 80  # int(input(\"port: \"))\r\n        path = \"\/\"  # input(\"path: \")\r\n        file_path = input(\"output file absolute path: \")\r\n        req = {\"host\": host, \"port\": port, \"path\": path}\r\n        threads.append(MyCrawler(req, file_path))\r\n        continue_input = input(\"add another? (y\/N) \") == \"y\"\r\n\r\n    for t in threads:\r\n        t.start()\r\n\r\n    current_thread = threading.currentThread()\r\n    for thread in threading.enumerate():\r\n        if thread is not current_thread:\r\n            thread.join()\r\n\r\n\r\n__main__()\r\n\r\n<\/pre>\n<p>Here, we are creating every thread as <em>daemon<\/em>, but we are enumerating every active thread by calling <code>threading.enumerate()<\/code> and joining every thread which is not the main one. The behavior remains the same:<\/p>\n<pre class=\"brush:bash\">\r\nhost: www.google.com.ar\r\noutput file absolute path: \/tmp\/google-ar.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.br\r\noutput file absolute path: \/tmp\/google-br.html\r\nadd another? (y\/N) y\r\nhost: www.google.com.co\r\noutput file absolute path: \/tmp\/google-co.html\r\nadd another? (y\/N) y\r\nhost: www.google.cl\r\noutput file absolute path: \/tmp\/google-cl.html\r\nadd another? (y\/N)\r\n(Crawler-www.google.com.ar) executing get request for parameters: {'port': 80, 'host': 'www.google.com.ar', 'path': '\/'}\r\n(Crawler-www.google.com.br) executing get request for parameters: {'port': 80, 'host': 'www.google.com.br', 'path': '\/'}\r\n(Crawler-www.google.com.co) executing get request for parameters: {'port': 80, 'host': 'www.google.com.co', 'path': '\/'}\r\n(Crawler-www.google.cl) executing get request for parameters: {'port': 80, 'host': 'www.google.cl', 'path': '\/'}\r\n(Crawler-www.google.com.ar) got 200 response http code\r\n(Crawler-www.google.cl) got 200 response http code\r\n(Crawler-www.google.com.br) got 200 response http code\r\n(Crawler-www.google.com.ar) saving 52980 bytes to \/tmp\/google-ar.html\r\n(Crawler-www.google.cl) saving 53088 bytes to \/tmp\/google-cl.html\r\n(Crawler-www.google.com.br) saving 53549 bytes to \/tmp\/google-br.html\r\n(Crawler-www.google.com.co) got 200 response http code\r\n(Crawler-www.google.com.co) saving 53117 bytes to \/tmp\/google-co.html\r\n<\/pre>\n<h3>2.5. Time Threads<\/h3>\n<p>Another thing that&#8217;s worthy of being pointed out, is the existence of the class <code>threading.Timer<\/code>. It is basically a subclass of <code>Thread<\/code> which, given a delay and a function, it executes the function after the delay has passed. Also, it can be cancelled at any point.<\/p>\n<pre class=\"brush:python\">\r\nimport threading\r\nimport time\r\nimport logging\r\n\r\nlogging.basicConfig(level=logging.DEBUG, format='(%(threadName)-10s) %(message)s',)\r\n\r\ndef delayed():\r\n    logging.debug('worker running')\r\n    return\r\n\r\nt1 = threading.Timer(3, delayed)\r\nt1.setName('t1')\r\nt2 = threading.Timer(3, delayed)\r\nt2.setName('t2')\r\n\r\nlogging.debug('starting timers')\r\nt1.start()\r\nt2.start()\r\n\r\nlogging.debug('waiting before canceling %s', t2.getName())\r\ntime.sleep(2)\r\nlogging.debug('canceling %s', t2.getName())\r\nt2.cancel()\r\nlogging.debug('done')\r\n<\/pre>\n<p>If we execute it:<\/p>\n<pre class=\"brush:bash\">\r\n(MainThread) starting timers\r\n(MainThread) waiting before canceling t2\r\n(MainThread) canceling t2\r\n(MainThread) done\r\n(t1        ) worker running\r\n<\/pre>\n<p>Here, we are creating two timers, both execute the same function after 3 seconds. Then we wait 2 seconds and cancel one of them. In the output we can see only one of the timers executed the delayed function.<\/p>\n<p>This is useful on scenarios where we need to execute some process if something didn&#8217;t happen in an interval of time, or even for scheduling.<\/p>\n<h3>2.6. Events: Communication Between Threads<\/h3>\n<p>Now, we all know that the idea of using threads is making tasks independent from each other, but some times we need for a thread to wait for an event caused by another. Python provides a way of signaling between threads. To experiment with this, we&#8217;ll make a race:<\/p>\n<p><span style=\"text-decoration: underline\"><em>race.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nimport threading\r\n\r\n\r\nclass Racer(threading.Thread):\r\n\r\n    def __init__(self, name, start_signal):\r\n        threading.Thread.__init__(self, name=name)\r\n        self.start_signal = start_signal\r\n\r\n    def run(self):\r\n        self.start_signal.wait()\r\n        print(\"I, {}, got to the goal!\".format(self.name))\r\n\r\n\r\nclass Race:\r\n\r\n    def __init__(self, racer_names):\r\n        self.start_signal = threading.Event()\r\n        self.racers = [Racer(name, self.start_signal) for name in racer_names]\r\n        for racer in self.racers:\r\n            racer.start()\r\n\r\n    def start(self):\r\n        self.start_signal.set()\r\n\r\n\r\ndef __main__():\r\n    race = Race([\"rabbit\", \"turtle\", \"cheetah\", \"monkey\", \"cow\", \"horse\", \"tiger\", \"lion\"])\r\n    race.start()\r\n\r\n\r\n__main__()\r\n<\/pre>\n<p>Here, we created a subclass of <code>Thread<\/code> called <code>Racer<\/code>, which on its run waits for the event to be set and then prints <em>&#8220;I, {name}, got to the goal!&#8221;<\/em>.<\/p>\n<p>We create a couple of threads, an then set the event, so they all try to start at the same time, the output is interesting:<\/p>\n<p><span style=\"text-decoration: underline\"><em>first run output<\/em><\/span><\/p>\n<pre class=\"brush:bash\">\r\nI, rabbit, got to the goal!\r\nI, lion, got to the goal!\r\nI, turtle, got to the goal!\r\nI, cheetah, got to the goal!\r\nI, monkey, got to the goal!\r\nI, cow, got to the goal!\r\nI, horse, got to the goal!\r\nI, tiger, got to the goal!\r\n<\/pre>\n<p><span style=\"text-decoration: underline\"><em>second run output<\/em><\/span><\/p>\n<pre class=\"brush:bash\">\r\nI, lion, got to the goal!\r\nI, turtle, got to the goal!\r\nI, monkey, got to the goal!\r\nI, horse, got to the goal!\r\nI, cow, got to the goal!\r\nI, tiger, got to the goal!\r\nI, cheetah, got to the goal!\r\nI, rabbit, got to the goal!\r\n<\/pre>\n<p>Here we can see how the rabbit won the first race, but ended last on the second. Either he got tired, or our event behaves just as we wanted it to behave.<\/p>\n<p>If we didn&#8217;t use the event, and start each thread in a loop, the first thread would have an advantage of milliseconds over the last one. And we all know every millisecond counts on computer times.<\/p>\n<h3>2.7. Locking Resources<\/h3>\n<p>Sometimes we have a couple threads accessing the same resource, and if it&#8217;s not thread safe, we don&#8217;t want threads to access it at the same time. One solution to this problem could be locking.<\/p>\n<p>A side note:  Python\u2019s built-in data structures (lists, dictionaries, etc.) are thread-safe as a side-effect of having atomic byte-codes for manipulating them (the GIL is not released in the middle of an update). Other data structures implemented in Python, or simpler types like integers and floats, don\u2019t have that protection.<\/p>\n<p>Let&#8217;s imagine, just to make a fun example, that dictionaries in python are not thread safe. We&#8217;ll make an on-memory repository and make a couple of threads read and write data to it:<\/p>\n<p><span style=\"text-decoration: underline\"><em>locking.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nimport random\r\nimport threading\r\nimport logging\r\n\r\nlogging.basicConfig(level=logging.INFO,\r\n                    format='[%(levelname)s] (%(threadName)-s) (%(module)-s) (%(funcName)-s) %(message)s',\r\n                    filename='\/tmp\/locking-py.log')\r\n\r\n\r\nclass Repository:\r\n    def __init__(self):\r\n        self.repo = {}\r\n        self.lock = threading.Lock()\r\n\r\n    def create(self, entry):\r\n        logging.info(\"waiting for lock\")\r\n        self.lock.acquire()\r\n        try:\r\n            logging.info(\"acquired lock\")\r\n            new_id = len(self.repo.keys())\r\n            entry[\"id\"] = new_id\r\n            self.repo[new_id] = entry\r\n        finally:\r\n            logging.info(\"releasing lock\")\r\n            self.lock.release()\r\n\r\n    def find(self, entry_id):\r\n        logging.info(\"waiting for lock\")\r\n        self.lock.acquire()\r\n        try:\r\n            logging.info(\"acquired lock\")\r\n            return self.repo[entry_id]\r\n        except KeyError:\r\n            return None\r\n        finally:\r\n            logging.info(\"releasing lock\")\r\n            self.lock.release()\r\n\r\n    def all(self):\r\n        logging.info(\"waiting for lock\")\r\n        self.lock.acquire()\r\n        try:\r\n            logging.info(\"acquired lock\")\r\n            return self.repo\r\n        finally:\r\n            logging.info(\"releasing lock\")\r\n            self.lock.release()\r\n\r\n\r\nclass ProductRepository(Repository):\r\n    def __init__(self):\r\n        Repository.__init__(self)\r\n\r\n    def add_product(self, description, price):\r\n        self.create({\"description\": description, \"price\": price})\r\n\r\n\r\nclass PurchaseRepository(Repository):\r\n    def __init__(self, product_repository):\r\n        Repository.__init__(self)\r\n        self.product_repository = product_repository\r\n\r\n    def add_purchase(self, product_id, qty):\r\n        product = self.product_repository.find(product_id)\r\n        if product is not None:\r\n            total_amount = product[\"price\"] * qty\r\n            self.create({\"product_id\": product_id, \"qty\": qty, \"total_amount\": total_amount})\r\n\r\n    def sales_by_product(self, product_id):\r\n        sales = {\"product_id\": product_id, \"qty\": 0, \"total_amount\": 0}\r\n        all_purchases = self.all()\r\n        for k in all_purchases:\r\n            purchase = all_purchases[k]\r\n            if purchase[\"product_id\"] == sales[\"product_id\"]:\r\n                sales[\"qty\"] += purchase[\"qty\"]\r\n                sales[\"total_amount\"] += purchase[\"total_amount\"]\r\n        return sales\r\n\r\n\r\nclass Buyer(threading.Thread):\r\n    def __init__(self, name, product_repository, purchase_repository):\r\n        threading.Thread.__init__(self, name=\"Buyer-\" + name)\r\n        self.product_repository = product_repository\r\n        self.purchase_repository = purchase_repository\r\n\r\n    def run(self):\r\n        for i in range(0, 1000):\r\n            max_product_id = len(self.product_repository.all().keys())\r\n            product_id = random.randrange(0, max_product_id + 1, 1)\r\n            qty = random.randrange(0, 100, 1)\r\n            self.purchase_repository.add_purchase(product_id, qty)\r\n\r\n\r\nclass ProviderAuditor(threading.Thread):\r\n    def __init__(self, product_id, purchase_repository):\r\n        threading.Thread.__init__(self, name=\"Auditor-product_id=\" + str(product_id))\r\n        self.product_id = product_id\r\n        self.purchase_repository = purchase_repository\r\n\r\n    def run(self):\r\n        logging.info(str(self.purchase_repository.sales_by_product(self.product_id)))\r\n\r\n\r\ndef __main__():\r\n    product_repository = ProductRepository()\r\n    purchase_repository = PurchaseRepository(product_repository)\r\n\r\n    input_another_product = True\r\n    while input_another_product:\r\n        description = input(\"product description: \")\r\n        price = float(input(\"product price: \"))\r\n        product_repository.add_product(description, price)\r\n        input_another_product = input(\"continue (y\/N): \") == \"y\"\r\n\r\n    buyers = [Buyer(\"carlos\", product_repository, purchase_repository),\r\n              Buyer(\"juan\", product_repository, purchase_repository),\r\n              Buyer(\"mike\", product_repository, purchase_repository),\r\n              Buyer(\"sarah\", product_repository, purchase_repository)]\r\n\r\n    for b in buyers:\r\n        b.start()\r\n        b.join()\r\n\r\n    for i in product_repository.all():\r\n        ProviderAuditor(i, purchase_repository).start()\r\n\r\n\r\n__main__()\r\n\r\n<\/pre>\n<p>As you see, both resources (purchases and products) are extending a class <code>Repository<\/code> which has locks for every access method (let&#8217;s assume every developer will know that he mustn&#8217;t access the repository&#8217;s dictionary directly).<\/p>\n<p>This lock will guarantee that only one thread at a time can access one repository. One thing to notice is how the lock is released in a <code>finally<\/code> block, you should be very careful with that. If you don&#8217;t put the release in a finally block, whenever an exception is raised and interrupts the function&#8217;s execution, your lock will not be released, and there will be no way to access that resource anymore.<\/p>\n<p>Now, let&#8217;s execute this code and input something like:<\/p>\n<pre class=\"brush:bash\">\r\nproduct description: a\r\nproduct price: 1\r\ncontinue (y\/N): y\r\nproduct description: b\r\nproduct price: 2\r\ncontinue (y\/N): y\r\nproduct description: c\r\nproduct price: 3\r\ncontinue (y\/N): y\r\nproduct description: d\r\nproduct price: 4\r\ncontinue (y\/N): y\r\nproduct description: e\r\nproduct price: 5\r\ncontinue (y\/N): y\r\nproduct description: f\r\nproduct price: 6\r\ncontinue (y\/N): y\r\nproduct description: g\r\nproduct price: 7\r\ncontinue (y\/N): y\r\nproduct description: h\r\nproduct price: 8\r\ncontinue (y\/N): y\r\nproduct description: i\r\nproduct price: 9\r\ncontinue (y\/N):\r\n<\/pre>\n<p>As you see, the logger was configured to output its log to a file. We don&#8217;t care about the logging of the <code>Buyer<\/code> threads, since they perform a thousand actions each. That log won&#8217;t be readable, BUT, <code>ProviderAuditor<\/code> threads will log some very interesting information. So we run <code>grep \"Auditor\" \/tmp\/locking-py.log<\/code> and see:<\/p>\n<pre class=\"brush:bash\">\r\n[INFO] (Auditor-product_id=0) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=0) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=0) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=1) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=0) (locking) (run) {'total_amount': 19850.0, 'product_id': 0, 'qty': 19850}\r\n[INFO] (Auditor-product_id=2) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=1) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=3) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=4) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=5) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=1) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=6) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=7) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=1) (locking) (run) {'total_amount': 41586.0, 'product_id': 1, 'qty': 20793}\r\n[INFO] (Auditor-product_id=2) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=2) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=2) (locking) (run) {'total_amount': 60294.0, 'product_id': 2, 'qty': 20098}\r\n[INFO] (Auditor-product_id=3) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=3) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=3) (locking) (run) {'total_amount': 86752.0, 'product_id': 3, 'qty': 21688}\r\n[INFO] (Auditor-product_id=4) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=8) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=4) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=4) (locking) (run) {'total_amount': 93960.0, 'product_id': 4, 'qty': 18792}\r\n[INFO] (Auditor-product_id=5) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=5) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=5) (locking) (run) {'total_amount': 109776.0, 'product_id': 5, 'qty': 18296}\r\n[INFO] (Auditor-product_id=6) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=6) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=6) (locking) (run) {'total_amount': 140945.0, 'product_id': 6, 'qty': 20135}\r\n[INFO] (Auditor-product_id=7) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=7) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=7) (locking) (run) {'total_amount': 164152.0, 'product_id': 7, 'qty': 20519}\r\n[INFO] (Auditor-product_id=8) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=8) (locking) (all) releasing lock\r\n[INFO] (Auditor-product_id=8) (locking) (run) {'total_amount': 182475.0, 'product_id': 8, 'qty': 20275}\r\n<\/pre>\n<p>There are our 8 <code>ProviderAuditor<\/code> threads, the first one to acquire the lock is the <em>Auditor-product_id=0<\/em>, then releases it and prints our sales. Then goes <em>Auditor-product_id=1<\/em> and <em>2, 3, 4 and 5<\/em> are waiting. And it goes on and on. Now (again, imagining python&#8217;s dictionaries are not thread safe) our resources are thread safe.<\/p>\n<p>Another side note here: Let&#8217;s imagine another scenario. We have a thread and a resource. The thread locks de resource to write some data, and in the middle, it needs to lock it again to read some other data without releasing the first lock. Well, we have a problem here&#8230; Normal <code>Lock<\/code> objects can not be acquired more than once, even by the same thread. Changing it is easy, just substitute <code>Lock<\/code> with <code>RLock<\/code>, which is a <em>Re-entrant Lock<\/em> and will provide access to a locked resource, only to the thread which performed the lock.<\/p>\n<p>Locks implement the context manager API and are compatible with the with statement. Using with removes the need to explicitly acquire and release the lock. Let&#8217;s modify our <code>Repository<\/code> class code to make it prettier:<\/p>\n<p><span style=\"text-decoration: underline\"><em>locking.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nclass Repository:\r\n    def __init__(self):\r\n        self.repo = {}\r\n        self.lock = threading.Lock()\r\n\r\n    def create(self, entry):\r\n        logging.info(\"waiting lock\")\r\n        with self.lock:\r\n            logging.info(\"acquired lock\")\r\n            new_id = len(self.repo.keys())\r\n            entry[\"id\"] = new_id\r\n            self.repo[new_id] = entry\r\n\r\n    def find(self, entry_id):\r\n        logging.info(\"waiting for lock\")\r\n        with self.lock:\r\n            try:\r\n                logging.info(\"acquired lock\")\r\n                return self.repo[entry_id]\r\n            except KeyError:\r\n                return None\r\n\r\n    def all(self):\r\n        logging.info(\"waiting for lock\")\r\n        with self.lock:\r\n            logging.info(\"acquired lock\")\r\n            return self.repo\r\n<\/pre>\n<p>And the behavior remains the same:<\/p>\n<pre class=\"brush:bash\">\r\n[INFO] (Auditor-product_id=0) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=0) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=1) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=0) (locking) (run) {'product_id': 0, 'total_amount': 19098.0, 'qty': 19098}\r\n[INFO] (Auditor-product_id=2) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=1) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=3) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=4) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=5) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=1) (locking) (run) {'product_id': 1, 'total_amount': 36344.0, 'qty': 18172}\r\n[INFO] (Auditor-product_id=6) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=2) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=7) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=8) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=9) (locking) (all) waiting for lock\r\n[INFO] (Auditor-product_id=2) (locking) (run) {'product_id': 2, 'total_amount': 57555.0, 'qty': 19185}\r\n[INFO] (Auditor-product_id=3) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=3) (locking) (run) {'product_id': 3, 'total_amount': 72292.0, 'qty': 18073}\r\n[INFO] (Auditor-product_id=4) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=4) (locking) (run) {'product_id': 4, 'total_amount': 88835.0, 'qty': 17767}\r\n[INFO] (Auditor-product_id=5) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=5) (locking) (run) {'product_id': 5, 'total_amount': 110754.0, 'qty': 18459}\r\n[INFO] (Auditor-product_id=6) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=6) (locking) (run) {'product_id': 6, 'total_amount': 129766.0, 'qty': 18538}\r\n[INFO] (Auditor-product_id=7) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=7) (locking) (run) {'product_id': 7, 'total_amount': 152576.0, 'qty': 19072}\r\n[INFO] (Auditor-product_id=8) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=8) (locking) (run) {'product_id': 8, 'total_amount': 150210.0, 'qty': 16690}\r\n[INFO] (Auditor-product_id=9) (locking) (all) acquired lock\r\n[INFO] (Auditor-product_id=9) (locking) (run) {'product_id': 9, 'total_amount': 160150.0, 'qty': 16015}\r\n<\/pre>\n<h3>2.8. Limiting Concurrent Access to Resources<\/h3>\n<p>Using a lock like that, ensures only one thread at a time can access a resource, but imagine a data base access. Maybe you have a connection pool of, at most, 100 connections. In this case you want concurrent access, but you don&#8217;t want more than a 100 threads to access this resource at once. <code>Semaphore<\/code> comes to help, it tells the <code>Lock<\/code> how many threads can acquire lock at once. Let&#8217;s modify our <code>ProviderAuditor<\/code> code to let at most 5 providers acquire lock at the same time:<\/p>\n<p><span style=\"text-decoration: underline\"><em>locking.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nclass ProviderAuditor(threading.Thread):\r\n    def __init__(self, product_id, purchase_repository):\r\n        threading.Thread.__init__(self, name=\"Auditor-product_id=\" + str(product_id))\r\n        self.product_id = product_id\r\n        self.purchase_repository = purchase_repository\r\n        self.semaphore = threading.Semaphore(5)\r\n\r\n    def run(self):\r\n        with self.semaphore:\r\n            logging.info(str(self.purchase_repository.sales_by_product(self.product_id)))\r\n<\/pre>\n<h3>2.9. Thread-Specific Data<\/h3>\n<p>We often need data to be only accessible from one thread (eg.: identifiers of the current process), python provides the <code>local()<\/code> method which returns data that is only accessible from one thread, here goes an example:<\/p>\n<p><span style=\"text-decoration: underline\"><em>process-identifier.py<\/em><\/span><\/p>\n<pre class=\"brush:python\">\r\nimport logging\r\nimport random\r\n\r\nfrom threading import Thread, local\r\n\r\nlogging.basicConfig(level=logging.INFO,\r\n                    format='[%(levelname)s] (%(threadName)-s) (%(module)-s) (%(funcName)-s) %(message)s',)\r\n\r\n\r\ndef my_method(data):\r\n    try:\r\n        logging.info(str(data.value))\r\n    except AttributeError:\r\n        logging.info(\"data does not have a value yet\")\r\n\r\n\r\nclass MyProcess(Thread):\r\n    def __init__(self):\r\n        Thread.__init__(self)\r\n\r\n    def run(self):\r\n        data = local()\r\n        my_method(data)\r\n        data.value = {\"process_id\": random.randint(0, 1000)}\r\n        my_method(data)\r\n\r\nfor i in range(0, 4):\r\n    MyProcess().start()\r\n\r\n<\/pre>\n<p>It&#8217;s pretty easy to use, a very useful. If you run it you&#8217;ll see something like:<\/p>\n<pre class=\"brush:bash\">\r\n[INFO] (Thread-1) (process-identifier) (my_method) data does not have a value yet\r\n[INFO] (Thread-1) (process-identifier) (my_method) {'process_id': 567}\r\n[INFO] (Thread-2) (process-identifier) (my_method) data does not have a value yet\r\n[INFO] (Thread-2) (process-identifier) (my_method) {'process_id': 477}\r\n[INFO] (Thread-3) (process-identifier) (my_method) data does not have a value yet\r\n[INFO] (Thread-3) (process-identifier) (my_method) {'process_id': 812}\r\n[INFO] (Thread-4) (process-identifier) (my_method) data does not have a value yet\r\n[INFO] (Thread-4) (process-identifier) (my_method) {'process_id': 981}\r\n<\/pre>\n<p>Every thread has to initialize <code>local().value<\/code>, data from other threads will never be available there.<\/p>\n<h2>3. Download the Code Project<\/h2>\n<p>This was an example on Threading in Python.<\/p>\n<div class=\"download\"><strong>Download<\/strong><br \/> You can download the full source code of this example here: <a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/12\/threading.zip\"><strong>python-threading<\/strong><\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in parallel so it would take half the time. This is not true in most cases. Using CPython, there is a mutex that prevents multiple native threads from &hellip;<\/p>\n","protected":false},"author":109,"featured_media":1651,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[53],"tags":[301,302,290,300],"class_list":["post-9776","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-concurrency","tag-parallelism","tag-python","tag-threading"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Python Threading \/ Concurrency Example - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Python Threading \/ Concurrency Example - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/sgvinci\" \/>\n<meta property=\"article:published_time\" content=\"2016-01-05T14:11:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-01-09T07:50:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Sebastian Vinci\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@sebastianvinci_\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sebastian Vinci\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"26 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\"},\"author\":{\"name\":\"Sebastian Vinci\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/06a43c63e373dff2e159bbc029b405aa\"},\"headline\":\"Python Threading \/ Concurrency Example\",\"datePublished\":\"2016-01-05T14:11:03+00:00\",\"dateModified\":\"2018-01-09T07:50:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\"},\"wordCount\":2121,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"keywords\":[\"concurrency\",\"parallelism\",\"python\",\"threading\"],\"articleSection\":[\"Python\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\",\"name\":\"Python Threading \/ Concurrency Example - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"datePublished\":\"2016-01-05T14:11:03+00:00\",\"dateModified\":\"2018-01-09T07:50:33+00:00\",\"description\":\"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/python\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Python Threading \/ Concurrency Example\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/06a43c63e373dff2e159bbc029b405aa\",\"name\":\"Sebastian Vinci\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/12d0233b49dd2a282330a987b16e81c3fbd4a8a8f5d5338348a6edd47cfff99e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/12d0233b49dd2a282330a987b16e81c3fbd4a8a8f5d5338348a6edd47cfff99e?s=96&d=mm&r=g\",\"caption\":\"Sebastian Vinci\"},\"description\":\"Sebastian is a full stack programmer, who has strong experience in Java and Scala enterprise web applications. He is currently studying Computers Science in UBA (University of Buenos Aires) and working a full time job at a .com company as a Semi-Senior developer, involving architectural design, implementation and monitoring. He also worked in automating processes (such as data base backups, building, deploying and monitoring applications).\",\"sameAs\":[\"http:\/\/www.webcodegeeks.com\/\",\"https:\/\/www.facebook.com\/sgvinci\",\"https:\/\/x.com\/sebastianvinci_\"],\"url\":\"https:\/\/www.webcodegeeks.com\/author\/sebastian-vinci\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Python Threading \/ Concurrency Example - Web Code Geeks - 2026","description":"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/","og_locale":"en_US","og_type":"article","og_title":"Python Threading \/ Concurrency Example - Web Code Geeks - 2026","og_description":"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in","og_url":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_author":"https:\/\/www.facebook.com\/sgvinci","article_published_time":"2016-01-05T14:11:03+00:00","article_modified_time":"2018-01-09T07:50:33+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","type":"image\/jpeg"}],"author":"Sebastian Vinci","twitter_card":"summary_large_image","twitter_creator":"@sebastianvinci_","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Sebastian Vinci","Est. reading time":"26 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/"},"author":{"name":"Sebastian Vinci","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/06a43c63e373dff2e159bbc029b405aa"},"headline":"Python Threading \/ Concurrency Example","datePublished":"2016-01-05T14:11:03+00:00","dateModified":"2018-01-09T07:50:33+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/"},"wordCount":2121,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","keywords":["concurrency","parallelism","python","threading"],"articleSection":["Python"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/","url":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/","name":"Python Threading \/ Concurrency Example - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","datePublished":"2016-01-05T14:11:03+00:00","dateModified":"2018-01-09T07:50:33+00:00","description":"Threads are processes which run in parallel to other threads. In a utopian scenario, if you split a big process in 2 threads, these threads will run in","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/python\/python-threading-concurrency-example\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Python","item":"https:\/\/www.webcodegeeks.com\/category\/python\/"},{"@type":"ListItem","position":3,"name":"Python Threading \/ Concurrency Example"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/06a43c63e373dff2e159bbc029b405aa","name":"Sebastian Vinci","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/12d0233b49dd2a282330a987b16e81c3fbd4a8a8f5d5338348a6edd47cfff99e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/12d0233b49dd2a282330a987b16e81c3fbd4a8a8f5d5338348a6edd47cfff99e?s=96&d=mm&r=g","caption":"Sebastian Vinci"},"description":"Sebastian is a full stack programmer, who has strong experience in Java and Scala enterprise web applications. He is currently studying Computers Science in UBA (University of Buenos Aires) and working a full time job at a .com company as a Semi-Senior developer, involving architectural design, implementation and monitoring. He also worked in automating processes (such as data base backups, building, deploying and monitoring applications).","sameAs":["http:\/\/www.webcodegeeks.com\/","https:\/\/www.facebook.com\/sgvinci","https:\/\/x.com\/sebastianvinci_"],"url":"https:\/\/www.webcodegeeks.com\/author\/sebastian-vinci\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/9776","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/109"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=9776"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/9776\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/1651"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=9776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=9776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=9776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}