DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Python Thread Tutorial (Part 2)

Python Thread Tutorial (Part 2)

Python threads sound like a way to make clothing out of snakes, but, thankfully for all those ophiophobia sufferers out there, it's about working with synchronicity.

Chandu Siva user avatar by
Chandu Siva
·
Nov. 16, 18 · Tutorial
Like (5)
Save
Tweet
Share
11.33K Views

Join the DZone community and get the full member experience.

Join For Free

Python Thread

In our previous article, we looked at threading methods in Python. In this article, we will look at daemon threads and locks.

Daemon Threads

So far, we have created a non-daemon thread. What is a daemon thread? When the main thread exits, it attempts to terminate all of its daemonic child threads.

Consider an example of GUI as shown below:
thread1
Consider that, by GUI input, some calculation is being performed in the background and the calculation is taking its time.

If you click the close button two courses of action can be performed.

  1. After clicking the close button, the whole GUI window closes.
  2. After clicking the close button, the GUI window will wait for the completion of background calculation.

If the first course of action is performed, then the daemon thread is being used in the background calculation. If the second course of action is performed, then a non-daemon thread is being used in background calculation.

Let us understand this with the help of some code:

import threading

import time

def n():

  print(“Non deamon start”)

print(“NOn daemoon exit”)

def d():

  print(”daemon start”)

time.sleep(5)

print(”daemon stop”)

t = threading.Thread(name = “non - daemon”, target = n)

d = threading.Thread(name = “daemon”, target = d)

d.setDaemon(True)

d.start()

t.start()

If method isDaemon() returns True, then the thread is a daemon thread. The syntax d.setDaemon(True) or d.daemon = Truecan be used to make daemon thread.

Let us see the output:
thread1
The daemon thread will take 5 seconds to complete its task, but main thread did not wait for the daemon thread. That’s why in the output there is no “daemon stop” statement. Now remove the time.sleep(5)  from the d() function and add it into the n() function.

See the code below:

import threading

import time

def n():

  print(“Non deamon start”)

time.sleep(5)

print(“NOn daemoon exit”)

def d():

  print(”daemon start”)

print(”daemon stop”)

t = threading.Thread(name = “non - daemon”, target = n)

d = threading.Thread(name = “daemon”, target = d)

d.setDaemon(True)

d.start()

t.start()

See the output:

thread1
In the above example, all print statements are executed. The main thread had to wait for the non-daemon process.

Note: If you use the join statement for the daemon thread, then the main thread has to wait for the completion of the daemon thread’s task.

Locks

Locks are the most fundamental synchronization mechanism provided by the threading module. A lock is in one of two states: locked or unlocked. If a thread attempts to hold a lock that’s already held by some other thread, the execution of the second thread is halted until the lock is released.

lock.acquire ():
Acquire a lock, blocks others until True (Default).

lock.locked():
Returns True if the lock is locked, otherwise False.

lock.release():
Unlocks the lock.

Let us see one example.

import threading

import time

lock = threading.Lock()

list1 = []

def fun1(a):

  lock.acquire()

list1.append(a)

lock.release()

for each in range(10):

  thread1 = threading.Thread(target = fun1, args = (each, ))

thread1.start()

print(“List1 is: “, list1)

The lock = threading.Lock() statement is used to create a lock object.

The main problem with the lock is that the lock does not remember which thread acquired the lock. Now two problems can arise.

See the code below.

import threading
import time

lock = threading.Lock()

import datetime

t1 = datetime.datetime.now()

def second(n):

  lock.acquire()

print(n)

def third():

  time.sleep(5)

lock.release()

print(“Thread3“)

th1 = threading.Thread(target = second, args = (“Thread1”, ))

th1.start()

th2 = threading.Thread(target = second, args = (“Thread2”, ))

th2.start()

th3 = threading.Thread(target = third)

th3.start()

th1.join()

th2.join()

th3.join()

t2 = datetime.datetime.now()

print(“Total time”, t2 - t1)

In the above code, a lock is acquired by thread1 and released by thread3. The thread2 is trying to acquire the lock.

Let us see the output.
thread1From the sequence of execution, it is clear that the lock acquired by thread1 got released by thread3.

Let's look at the second problem.

import threading

lock = threading.Lock()

def first(n):

  lock.acquire()

a = 12 + n

lock.release()

print(a)

def second(n):

  lock.acquire()

b = 12 + n

lock.release()

print(b)

def all():

  lock.acquire()

first(2)

second(3)

lock.release()

th1 = threading.Thread(target = all)

th1.start()

When you run the above code, a deadlock will occur. In the function, all threads will acquire a lock, after acquiring the lock the first function will be called. The thread will see the lock.acquire() statement. As this lock itself is acquired by the same thread. But the lock does not remember the thread which acquired it.

In order to overcome the above problem, we use a re-entrant lock (RLock).

Just replace threading.Lock with threading.RLock. 

threading.RLock() — A factory function that returns a new re-entrant lock object. A re-entrant lock must be released by the thread that acquired it. Once a thread has acquired a re-entrant lock, the same thread may acquire it again without blocking; the thread must release it once for each time it has acquired it.

Lock vs Rlock

The main difference is that a lock can only be acquired once. It cannot be acquired again until it is released (after it’s been released, it can be re-acquired by any thread).

An RLock, on the other hand, can be acquired multiple times, by the same thread. It needs to be released the same number of times in order to be “unlocked.”

Another difference is that an acquired Lock can be released by any thread, while an acquired RLock can only be released by the thread which acquired it.

GIL

Thread-based parallelism is the standard way of writing parallel programs. However, the Python interpreter is not fully thread-safe. In order to support multi-threaded Python programs, a global lock called the Global Interpreter Lock (GIL) is used. This means that only one thread can execute the Python code at the same time; Python automatically switches to the next thread after a short period of time or when a thread does something that may take a while. The GIL is not enough to avoid problems in your own programs. Although, if multiple threads attempt to access the same data object, it may end up in an inconsistent state.

Let us see the example.

import datetime

def count(n):

  t1 = datetime.datetime.now()

while n > 0:

  n = n - 1

t2 = datetime.datetime.now()

print(t2 - t1)

count(100000000)

In the above code, the count function is being run the main thread. Let see the time taken by the thread.

thread1I ran the code three times, every time I got a similar result.

Let us create two thread, see the code below.

import datetime

from threading
import Thread

def count(n):

  while n > 0:

  n = n - 1

def count1(n):

  while n > 0:

  n = n - 1

t1 = datetime.datetime.now()

thread1 = Thread(target = count, args = (100000000, ))

thread2 = Thread(target = count1, args = (100000000, ))

thread1.start()

thread2.start()

thread1.join()

thread2.join()

t2 = datetime.datetime.now()

print(t2 - t1)

In the above, two threads have been created to be run in parallel.

Let us see the result.
thread1

You can the above code took almost 10 seconds which is the double of the previous program, it means, only the main thread act as multithreading. But, in the above experiment, we can conclude that Multithreading is defined as the ability of a processor to execute multiple threads concurrently.

In a simple, single-core CPU, it is achieved using frequent switching between threads. This is called context switching. In context switching, the state of a thread is saved and state of another thread is loaded whenever any interrupt (due to I/O or manually set) takes place. Context switching takes place so frequently that all the threads appear to be running parallelly (this is called multitasking).

I hope you enjoyed the article.

Threading Python (language)

Published at DZone with permission of Chandu Siva. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Top 5 Java REST API Frameworks
  • Using JSON Web Encryption (JWE)
  • The Role of Data Governance in Data Strategy: Part II
  • Why It Is Important To Have an Ownership as a DevOps Engineer

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: