Concurrence refers to the a system’s ability to perform multiple tasks simultaneously. Concomitance is therefore an essential feature that enables developers to create applications capable of performing several operations simultaneously, thereby improving the software’s performance and efficiency.
Concurrency can be achieved by using various execution threads, which are independent processing units which can execute tasks concurrently. Programming languages provide various ways of working with threads, enabling developers to implement concurrency effectively in their applications.
An example of concurrency and threads
One of the most common ways of working with concurrency – using Python as an example – is by using the `Thread` class or implementing the `Runnable` interface. For example, you can create a class that extends the `Thread` class and override the `run()` method to define the task to be executed in a separate thread. Basic example:
import threading
# Define a function to be executed by a thread
def task():
print(«Hello from the thread»)
# Create a thread and execute the task function
thread = threading.Thread(target=task)
thread.start()
In this example, a function called `tarea()` is defined which simply prints a message to the console. A `Thread` object is then created by specifying the `tarea` function as the thread’s target, and the thread is started by calling the `start()` method.
Concurrency with ThreadPoolExecutor
In addition to the threading module, Python also provides the concurrent.futures module, which offers a high-level interface for working with concurrency and parallelism. This module includes classes such as ThreadPoolExecutor and ProcessPoolExecutor, which allow functions to be executed concurrently using pools of threads or processes. Here is an example of how to use `ThreadPoolExecutor`:
from concurrent.futures import ThreadPoolExecutor
# Define a function to be executed by a thread
def task():
print(«Hello from the thread»)
# Create a ThreadPoolExecutor with a pool of 5 threads
executor = ThreadPoolExecutor(max_workers=5)
# Running multiple tasks using the ThreadPoolExecutor
for _ in range(10):
executor.submit(task)
# Shut down the ThreadPoolExecutor once all tasks have been completed
executor.shutdown()
In this example, a ThreadPoolExecutor is created with a maximum of 5 threads in the pool. The submit() method is then used to submit tasks to be executed by the thread pool. Once all the tasks have been submitted, the shutdown() method is called to shut down the ThreadPoolExecutor.
Now, if we had to make multiple requests to different websites and process the data, we can use a “ThreadPoolExecutor” to send the requests in parallel and then process the results concurrently.
from concurrent.futures import ThreadPoolExecutor
import requests
# Function for making an HTTP request
def make_request(url):
response = requests.get(url)
return response.text
urls = [‘https://sitio1.com’, ‘https://sitio2.com’, ‘https://sitio3.com’]
# Create a ThreadPoolExecutor
with ThreadPoolExecutor() as executor:
# Send requests in parallel
results = executor.map(make_request, urls)
# Process the results
for result in results:
process(result)
Generally speaking, concurrency enables developers to create faster and more efficient applications by making effective use of system resources. This can be achieved by using the threading module to work with threads, although this does add an extra layer of complexity to the system’s programming.