Unit 4
Unit 4
Unit-4
C Program- Not a FPL
#include <stdio.h>
int main() {
int num1, num2, sum;
return 0;
}
Haskell - FPL
main :: IO ()
main = do
putStrLn "Enter the first number: "
input1 <- getLine
putStrLn "Enter the second number: "
input2 <- getLine
let num1 = read input1 :: Int
let num2 = read input2 :: Int
let sum = num1 + num2
putStrLn $ "The sum of " ++ show num1 ++ " and " ++ show num2 ++ " is " ++ show sum
C Vs Haskell
• Lets have a look at C and Haskell prog to add
content of two matrices to understand the
differences between two programs and
approaches of these languages.
• C Program:
– Procedural Programming: The C program follows a procedural programming paradigm, which relies on
procedures and functions.
– Explicit Type Declarations: In C, data types need to be explicitly declared, and there is no type
inference.
– Fixed Array Sizes: C requires specifying fixed array sizes in advance, making it less flexible when
dealing with matrices of variable dimensions.
– Manual Memory Management: C requires manual memory management, including dynamic
allocation and deallocation of memory.
– Imperative Style: C uses an imperative style of programming, which involves a sequence of statements
that modify the program's state.
– Lower-Level Language: C is closer to the machine and provides fine-grained control over memory and
hardware, which can be advantageous for performance-critical applications.
FP - Introduction
• treats computation as the evaluation of
mathematical functions
– The scope of a variable is determined by its location in the code, specifically where it is
declared.
– In languages like Python, Java, or C, the scope of a variable is determined by the block
of code in which it is declared.
– Easier to understand and reason about, as the scope is evident from the code
structure.
def outer_function():
x = 10
def inner_function():
print(x) # inner_function can access x from outer_function's scope
inner_function()
outer_function()
Dynamic Scope
• Dynamic Scope
– Dynamic scope is determined by the call stack during runtime.
– In languages that use dynamic scope, a function can access variables from
the calling function's scope, not just its own scope.
(defun outer-function ()
(let ((x 10))
(inner-function)))
(defun inner-function ()
(print x)) ; inner-function can access x from the calling
function's scope
(outer-function)
Binding Values and Functions
• Binding refers to the association of an identifier with a value or
function.
• Concurrency:
– Threads allow multiple tasks to run concurrently, which is particularly useful for
applications with parallelizable tasks.
• Shared Resources:
– Threads within the same process share the same memory space and resources. This
makes communication between threads more straightforward but also requires
careful synchronization to avoid conflicts.
• Thread Lifecycle:
– Threads typically go through various states, including creation, running, waiting, and
termination.
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getId() + " Value " + i);
}
}
}
– Data Inconsistency:
• When multiple processes or threads access shared data concurrently, it can result in
data inconsistency. For example, if one thread is updating a variable while another is
reading it, the reading thread may get an intermediate or inconsistent value.
• Deadlocks:
– Deadlocks can occur when multiple processes or threads are waiting for each other
to release resources, creating a circular dependency. Synchronization mechanisms
help prevent and resolve deadlocks.
• Resource Contention:
– Shared resources, such as files, database connections, or hardware devices, can
become points of contention. Synchronization ensures that processes or threads
access these resources in a controlled and mutually exclusive manner.
• Orderly Communication:
– Synchronization is essential for processes or threads to communicate with each
other in an orderly way. It allows them to exchange information and coordinate
their activities without conflicting with each other.
• Critical Sections:
– Critical sections are portions of code where shared resources are accessed and
modified. Synchronization mechanisms, such as locks or semaphores, help enforce
mutual exclusion in critical sections to avoid conflicts.
• Consistent State:
– In applications where maintaining a consistent state is crucial, synchronization
helps ensure that multiple processes or threads collectively achieve a consistent
and correct result.
• Preventing Resource Exhaustion:
– Synchronization helps prevent scenarios where multiple processes or threads try
to acquire the same resource simultaneously, leading to resource exhaustion or
contention.
• Correctness and Predictability:
– Synchronization ensures the correctness and predictability of program behavior. It
helps avoid unexpected outcomes that could arise from uncontrolled interactions
between concurrent processes or threads.
• Efficient Resource Utilization:
– Synchronization allows for efficient and controlled use of shared resources,
preventing wasteful contention and ensuring that resources are utilized optimally.
Synchronization Monitors
• Synchronization monitors, or monitors, are a high-level synchronization mechanism
• combines the notion of a data structure with a set of procedures, or functions, that
operate on that data structure.
• Condition Variables:
– Monitors often include condition variables that allow threads to wait for a specific condition to be
satisfied before proceeding.
– Threads can signal or broadcast to wake up waiting threads when certain conditions are met.
• Encapsulation:
– Monitors encapsulate both data and the procedures that operate on that data, providing a clear and
modular structure.
– This encapsulation simplifies the implementation of critical sections and reduces the likelihood of
errors.
• Atomicity:
– Procedures within a monitor are typically executed atomically, meaning they appear to execute
instantaneously without interruption.
– This atomicity simplifies reasoning about the behavior of the monitor.
public class MonitorExample {
public static void main(String[] args) {
// Shared resource
SharedResource sharedResource = new SharedResource();
try {
// Wait for both threads to finish
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
• The main method is the entry point of the program, and it runs in the main
thread.
• The main thread starts both Thread-1 and Thread-2 using the start method.
• The main thread then waits for both Thread-1 and Thread-2 to finish using
the join method.
• After both threads have completed, the main thread proceeds to print the
message "Main thread exiting.“
• Thread Lifecycle:
– Threads in Java go through different states, including NEW, RUNNABLE, BLOCKED,
WAITING, TIMED_WAITING, and TERMINATED.
– The start() method is used to begin the execution of a thread, and the run() method
contains the code to be executed by the thread.
• Synchronization
– Java provides synchronization mechanisms to control
access to shared resources and prevent race conditions.
– The synchronized keyword can be used to create
synchronized methods or blocks, ensuring only one
thread can access the critical section at a time.