{"id":18260,"date":"2021-06-20T19:30:23","date_gmt":"2021-06-20T19:30:23","guid":{"rendered":"https:\/\/www.askpython.com\/?p=18260"},"modified":"2021-06-22T15:47:28","modified_gmt":"2021-06-22T15:47:28","slug":"synchronization-in-python","status":"publish","type":"post","link":"https:\/\/www.askpython.com\/python\/examples\/synchronization-in-python","title":{"rendered":"Synchronization in Python &#8211; Synchronize Threads in Python"},"content":{"rendered":"\n<p>Let&#8217;s talk about synchronization in Python. <a href=\"https:\/\/www.askpython.com\/python-modules\/multithreading-in-python\" data-type=\"post\" data-id=\"6745\">Multithreading<\/a> allows your computer to perform actions in parallel, utilizing multiple cores\/ multiple CPUs present on your system. However, when it comes to reading and updating shared variables at the same time, it can lead to erroneous results. We will learn how to synchronize threads to give correct results.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Understanding the Race Condition in Multithreading<\/h2>\n\n\n\n<p>When 2 or more threads try to access a shared resource simultaneously and change the data, the final value of such variables is unpredictable. This is because the thread scheduling algorithm can swap between threads at any time and you don&#8217;t know which thread will execute first. This scenario is called a race condition.<\/p>\n\n\n\n<p>Let us take an example where we transfer some amount from one bank account to another using threads. We will create 100 threads to transfer 1 unit from account1 to account2.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport threading\nimport time\n\nclass BankAccount():\n  def __init__(self, name, balance):\n    self.name = name\n    self.balance = balance\n\n  def __str__(self):\n    return self.name\n\n# These accounts are our shared resources\naccount1 = BankAccount(&quot;account1&quot;, 100)\naccount2 = BankAccount(&quot;account2&quot;, 0)\n\nclass BankTransferThread(threading.Thread):\n  def __init__(self, sender, receiver, amount):\n    threading.Thread.__init__(self)\n    self.sender = sender\n    self.receiver = receiver\n    self.amount = amount\n  \n  def run(self):\n    sender_initial_balance = self.sender.balance\n    sender_initial_balance -= self.amount\n    # Inserting delay to allow switch between threads\n    time.sleep(0.001)\n    self.sender.balance = sender_initial_balance\n    \n    receiver_initial_balance = self.receiver.balance\n    receiver_initial_balance += self.amount\n    # Inserting delay to allow switch between threads\n    time.sleep(0.001)\n    self.receiver.balance = receiver_initial_balance\n\nif __name__ == &quot;__main__&quot;:\n  \n  threads = &#x5B;]\n\n  for i in range(100):\n    threads.append(BankTransferThread(account1, account2, 1))\n\n  for thread in threads:\n    thread.start()\n\n  for thread in threads:\n    thread.join()\n\n  print(account1.balance)\n  print(account2.balance)\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\naccount1 98\naccount2 3\n<\/pre><\/div>\n\n\n<p>Initially, account1 has 100 units and account2 has 0 units. <\/p>\n\n\n\n<p>After 100 transfers of 1 unit, account1 should have 0 units and the account2 should have 100 units. However, we got different results. If we run this multiple times, we will get different results.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Synchronization in Python &#8211; Different Methods to Synchronize Threads<\/h2>\n\n\n\n<p>Lets see how to synchronize threads to avoid race conditions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Lock Objects<\/h3>\n\n\n\n<p>A Lock object is the most basic synchronization primitive which is not owned by a particular thread when locked. A Lock object does not keep information about which thread has a permit of the lock and any thread can release the lock.<\/p>\n\n\n\n<p>The Lock object is in one of the 2 states, &#8220;locked&#8221; and &#8220;unlocked&#8221;. When the Lock object is created, it is in the &#8220;unlocked&#8221; state. There are only 3 methods in the lock object:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><em>acquire():<\/em><\/strong> This method changes the Lock object from an &#8220;unlocked&#8221; state to a &#8220;locked&#8221; state and allows the calling thread to continue execution. If the Lock object is already in a &#8220;locked&#8221; state, the calling thread will be blocked until the lock comes in an &#8220;unlocked&#8221; state.<\/li><li><em><strong>release():<\/strong><\/em> This method changes the Lock object state from &#8220;locked&#8221; to &#8220;unlocked&#8221; state. If the Lock object is already in an &#8220;unlocked&#8221; state, a <code>RuntimeError<\/code> is raised. The method can be called from any thread, not only the thread which has acquired the lock.<\/li><li><strong><em>locked(): <\/em><\/strong>This method returns true if the Lock object is acquired.<\/li><\/ul>\n\n\n\n<p>Let&#8217;s see how to use Lock object to add synchronization in Python to our bank transfer example.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; highlight: [17,27,41]; title: ; notranslate\" title=\"\">\nimport threading\nimport time\n\nclass BankAccount():\n  def __init__(self, name, balance):\n    self.name = name\n    self.balance = balance\n\n  def __str__(self):\n    return self.name\n\n# These accounts are our shared resources\naccount1 = BankAccount(&quot;account1&quot;, 100)\naccount2 = BankAccount(&quot;account2&quot;, 0)\n\n# Creating lock for threads\nlock = threading.Lock()\n\nclass BankTransferThread(threading.Thread):\n  def __init__(self, sender, receiver, amount):\n    threading.Thread.__init__(self)\n    self.sender = sender\n    self.receiver = receiver\n    self.amount = amount\n  \n  def run(self):\n    lock.acquire()\n    \n    sender_initial_balance = self.sender.balance\n    sender_initial_balance -= self.amount\n    # Inserting delay to allow switch between threads\n    time.sleep(0.001)\n    self.sender.balance = sender_initial_balance\n    \n    receiver_initial_balance = self.receiver.balance\n    receiver_initial_balance += self.amount\n    # Inserting delay to allow switch between threads\n    time.sleep(0.001)\n    self.receiver.balance = receiver_initial_balance\n    \n    lock.release()\n    \n\nif __name__ == &quot;__main__&quot;:\n  \n  threads = &#x5B;]\n\n  for i in range(100):\n    threads.append(BankTransferThread(account1, account2, 1))\n\n  for thread in threads:\n    thread.start()\n\n  for thread in threads:\n    thread.join()\n\n  print(account1.name, account1.balance)\n  print(account2.name, account2.balance)\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\naccount1 0\naccount2 100\n<\/pre><\/div>\n\n\n<p>The Lock object does not know which thread calls the <code>acquire()<\/code> method and any thread can call <code>release()<\/code> on the lock which can take permit from the thread that calls the <code>acquire()<\/code>. <\/p>\n\n\n\n<p>Also if same thread calls <code>acquire()<\/code> method again without <code>release()<\/code>, the thread will be in the deadlock state.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport threading\n\nlock = threading.Lock()\n\ndef funcA():\n  print(&quot;In A, acquiring lock&quot;)\n  lock.acquire()\n  \n  print(&quot;In A, lock acquired&quot;)\n  \n  print(&quot;In A, lock acquiring again and entering into deadlock&quot;)\n  lock.acquire()\n  \n  print(&quot;In A, releasing lock&quot;)\n  lock.release()\n  \n  print(&quot;In A, lock released&quot;)\n\ndef funcB():\n  print(&quot;In B, releasing lock acquired by A&quot;)\n  lock.release()\n  \n  print(&quot;In B, lock released&quot;)\n\nif __name__ == &quot;__main__&quot;:\n  thread1 = threading.Thread(target=funcA)\n  thread2 = threading.Thread(target=funcB)\n\n  thread1.start()\n  thread2.start()\n\n  thread1.join()\n  thread2.join()\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nIn A, acquiring lock\nIn A, lock acquired\nIn A, lock acquiring again and entering into deadlock\nIn B, releasing lock acquired by A\nIn B, lock released\nIn A, releasing lock\nIn A, lock released\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">2. RLock Objects<\/h3>\n\n\n\n<p>A reentrant lock (RLock) is another synchronization primitive that may be acquired multiple times by the same thread without entering into a deadlock state. The RLock object knows which thread has the permission of the lock and the same thread can unlock it. <\/p>\n\n\n\n<p>The RLock object is in one of the 2 states, &#8220;locked&#8221; and &#8220;unlocked&#8221;. When the RLock object is created, it is in the &#8220;unlocked&#8221; state. There are only 2 methods in the RLock object:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><em>acquire():<\/em><\/strong> This method changes the Lock object from an &#8220;unlocked&#8221; state to a &#8220;locked&#8221; state and allows the calling thread to continue execution. If the same thread calls this method again, it increases the recursion level by one. To fully release the lock, the same thread needs to call <code>release()<\/code> the same number of times. If another thread calls this method in a &#8220;locked&#8221; state, the thread will be blocked.<\/li><li><em><strong>release():<\/strong><\/em> This method releases the lock and decreases the recursion level by one. If the recursion level becomes 0 after decrement, the lock state is changed to an &#8220;unlocked&#8221; state. If after the decrement the recursion level is still nonzero, the lock remains &#8220;locked&#8221; and owned by the calling thread. If the RLock object is already in an &#8220;unlocked&#8221; state, a <code>RuntimeError<\/code> is raised.<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport threading\n\nlock = threading.RLock()\n\ndef funcA():\n  print(&quot;In A, acquiring lock&quot;)\n  lock.acquire()\n  \n  print(&quot;In A, lock acquired, recursion level = 1&quot;)\n  \n  print(&quot;In A, acquiring lock again&quot;)\n  lock.acquire()\n  \n  print(&quot;In A, lock acquired again, recursion level = 2&quot;)\n  \n  print(&quot;In A, releasing lock&quot;)\n  lock.release()\n  \n  print(&quot;In A, lock released, recursion level = 1&quot;)\n  \n\ndef funcB():\n  print(&quot;In B, trying to acquire lock, but A released only once, so entering in deadlock state&quot;)\n  lock.acquire()\n  print(&quot;This statement won&#039;t be executed&quot;)\n  \nif __name__ == &quot;__main__&quot;:\n  thread1 = threading.Thread(target=funcA)\n  thread2 = threading.Thread(target=funcB)\n\n  thread1.start()\n  thread2.start()\n\n  thread1.join()\n  thread2.join()\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nIn A, acquiring l\nIn A, lock acquired, recursion level = 1\nIn A, acquiring lock again\nIn A, lock acquired again, recursion level = 2\nIn A, releasing lock\nIn A, lock released, recursion level = 1\nIn B, trying to acquire lock, but A released only once, so entering in deadlock state\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">3. Semaphores<\/h3>\n\n\n\n<p>Semaphore is simply a variable that is non-negative and shared between threads. While the <code>Lock<\/code> and <code>RLock<\/code> objects allow only one thread to execute, Semaphore allows more than one thread to execute at a time. Semaphores are used to protect resources that have a limited capacity by specifying the number of threads allowed to execute when creating a Semaphore object. If this initial count is 1, Semaphores can help in the synchronization of threads.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><em>Creating a Semaphore:<\/em><\/strong> To create a Semaphore object, call <code>Semaphore(count)<\/code> in threading module, where <code>count<\/code> is the number of threads allowed to access simultaneously. The default value of the count is 1.<\/li><li><strong><em>acquire():<\/em><\/strong> When a thread calls this method<ul><li>If the count value of Semaphore is 0, the thread is blocked until awoken by a call to\u00a0<code>release()<\/code>.<\/li><li>If the count value of Semaphore is greater than 0, it is decremented by 1 and the thread continues its execution.<\/li><\/ul><\/li><li><strong><em>release():<\/em><\/strong> This method increments the count value by 1. If any thread is blocked on <code>acquire()<\/code>, it unblocks one of the threads.<\/li><\/ul>\n\n\n\n<p>Let us take an example, where 10 threads are trying to read a shared resource, but we limit the concurrent reads on shared resource to 3 using Semaphores.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport threading\nimport time\n\nread_mutex = threading.Semaphore(3)\n\n# Our shared resource\ndata = &quot;A Data Stream&quot;\n\nclass ReaderThread(threading.Thread):\n  def __init__(self):\n    threading.Thread.__init__(self)\n\n  def run(self):\n    \n    read_mutex.acquire()\n    \n    output = self.getName() + &quot; starts reading&quot;\n    print(output)\n    \n    # threads take time to read a data\n    time.sleep(0.5)\n    some_data = data\n    \n    output = self.getName() + &quot; ends reading&quot;\n    print(output)\n    \n    read_mutex.release()\n  \n  \nif __name__ == &quot;__main__&quot;:\n  \n  threads = &#x5B;]\n  for i in range(10):\n    threads.append(ReaderThread())\n\n  for thread in threads:\n    thread.start()\n\n  for thread in threads:\n    thread.join()\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nThread-6 starts reading\nThread-7 starts reading\nThread-8 starts reading\nThread-8 ends readingThread-7 ends readingThread-6 ends reading\n\n\nThread-10 starts reading\nThread-11 starts reading\nThread-9 starts reading\nThread-11 ends readingThread-10 ends reading\nThread-12 starts reading\n\nThread-13 starts reading\nThread-9 ends reading\nThread-14 starts reading\nThread-13 ends readingThread-12 ends reading\n\nThread-15 starts reading\nThread-14 ends reading\nThread-15 ends reading\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>In this tutorial, we have learned synchronization in Python to avoid race conditions by using the <a href=\"https:\/\/www.askpython.com\/python-modules\/multiprocessing-in-python\" data-type=\"post\" data-id=\"11383\">threading module in Python<\/a>. We used Lock, RLock, and Semaphores to achieve synchronization in Python. Thanks for reading!!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s talk about synchronization in Python. Multithreading allows your computer to perform actions in parallel, utilizing multiple cores\/ multiple CPUs present on your system. However, when it comes to reading and updating shared variables at the same time, it can lead to erroneous results. We will learn how to synchronize threads to give correct results. [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":18323,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":["post-18260","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-examples"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/posts\/18260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/comments?post=18260"}],"version-history":[{"count":0,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/posts\/18260\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/media\/18323"}],"wp:attachment":[{"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/media?parent=18260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/categories?post=18260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.askpython.com\/wp-json\/wp\/v2\/tags?post=18260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}