Lecture Notes on Concurrency - Programming Languages I | COP 4020, Study notes of Programming Languages

Material Type: Notes; Class: Programming Languages I; Subject: Computer Programming; University: University of Central Florida; Term: Spring 2007;

Typology: Study notes

Pre 2010

Uploaded on 11/08/2009

koofers-user-4kb
koofers-user-4kb 🇺🇸

5

(1)

9 documents

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Concurrency
Chapter
13.1 - 13.3
Concurrent Programs
Concurrent programming was introduced by the Programming Languages PL/1 and Algol 66.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- example -
Program
. . .
. . .
Procedure T1
. . .
. . .
. . .
Procedure T2
. . .
. . .
. . .
. . .
. . .
task T1 ( ) // new activation record
task T2 ( ) // new activation record
. . .
Although it seems to the user that the main program and the 2 sub programs are running at the same
time, in reality there is only one program in execution (in a single CPU setup):
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download Lecture Notes on Concurrency - Programming Languages I | COP 4020 and more Study notes Programming Languages in PDF only on Docsity!

Concurrency

Chapter 13.1 - 13. Concurrent Programs Concurrent programming was introduced by the Programming Languages PL/1 and Algol 66.

- example -

Program ... ... Procedure T ... ... ... Procedure T ... ... ... ... ... task T1 ( ) // new activation record task T2 ( ) // new activation record ... Although it seems to the user that the main program and the 2 sub programs are running at the same time, in reality there is only one program in execution (in a single CPU setup):

Processes Important Term Process – A program in execution. A process is a program in execution. It consists of a program's PCB and Executable: When there is a context switch the PCB of the process loaded in the CPU must be saved back to memory, and the PCB of the next process must be loaded. Context Switching can be done faster though using threads. Important Term Thread – A unit of execution of a process A thread is like a mini process created by a process or another thread. Officially it is a unit of execution of a process. It requires less information to be transferred to the CPU and therefore less time is needed to complete the context switch.

Critical Sections One major issue that arises when working with concurrency is when more then one process or thread tries to access the same data (called Shared Data). Important Term Shared Data – Data that more then one process or thread has access to. Important Term Critical Section – Code that accesses and/or uses Shared Data. Any block of code that access the Shared Data is called a Critical Section. This is because depending on the order the the different processes/threads execute the outcome of the critical section may be different.

- example -

If you have 2 processes running, both that access the same variable x. Each process adds 100 to x. If x=0 at the start, then x should equal 200 when the 2 processes complete execution. This is not always the case though...

If at this point the OS scheduler interrupts process1 and makes a context switch, the register information from the CPU is saved to the PCB. If process2 then starts and completes... 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Hardware Solution One method for dealing with race conditions, developed by IBM, is a hardware instruction called “test & set”, which is executed atomically (it is done in one step, it cannot be interrupted midway). Before entering a critical section a programmer would add the call, lock(L) to tell the hardware that the code is entering a critical section that corresponds to some variable L. IF another process has already called lock(L) , then the current process is forced to wait before continuing. When a process has finished leaving it's critical section, the programmer adds unload(L). The next time a process that was waiting the shared resource (that corresponds to L) is run, it is allowed to enter the critical section.

The problem with this method is that the CPU cannot tell the difference between a process is waiting for another processes to leave a critical section from a process that is just running normally. So if that process is given 50 milliseconds to run each time it is loaded, a large amount of processing power is wasted just waiting around. This waiting time that a process goes through is called Busy Waiting. Important Term Busy Waiting – When a process is stuck in a loop waiting for another process to leave a critical section.

**Semaphores**

A better solution was developed by Djistra. He created variables that he called Semaphores. A semaphore has a value and a pointer. It looks and works much like the “test & set” method except it is more powerful and reduces busy waiting. This is because when a process needs to wait for another process (when that process is using a shared resource the other one wants to use) it is taken out of the OS's process scheduler and added in a waiting queue in the semaphore (that is what the pointer is for). That way CPU cycles aren't waited on a waiting process, and when the process using the critical section finishes, there is a pointer in the semaphore pointing to the next waiting process that wants to enter its critical section.

P and V functions: P(semaphore s) { s-- if (s < 0) { // find current process // remove from process queue // add to semaphore queue } // allow into critical section } V(semaphore s) { s++ if (s <= 0) { // dequeue process from semaphore queue // add to ready queue } } Everything in the two IF statements are made atomic using interrupt masking (turning off any interrupt flags that might happen). Example: initial state:

Process A enters the critical section after calling P(s). A is then context switched out while still in the critical section:

Producer/Consumer Problem The producer/consumer problem deals with a producer (or producers) that is making something, and a consumer (or consumers) that is using that something. It is an activity in a major subject of shared resources, Synchronization. When the producer finishes a unit of product it is placed on a shelf (in a buffer) where the consumer can pick it up. If the producer is faster than the consumer, the shelf can fill up and the producer must stop until the consumer takes another item. If the consumer is faster than the producer, the shelf can become empty and the consumer must stop until the producer places another item on the shelf. Therefore, in computing the producer must wait if the buffer is full, and the consumer must wait of the buffer is empty. The buffer is a shared resource. Also, since the buffer is a shared resource, the there is a critical section in both the producers (when it adds product to the buffer) and the consumer (when it takes product from the buffer). The solution to this problem is to have 3 semaphores: The first is the mutex semaphore. It protects the buffer itself so that only one process may access the buffer at a time. It therefore is set to 1 like a regular shared resource semaphore. The producer and consumer both call P(mutex) before accessing the buffer, and V(mutex) upon leaving the buffer.

The second is the full semaphore. It is set to the size of the buffer. Every time the producer adds an item it calls P(full) and every time the consumer takes an item it calls V(full). If the size of the buffer is n , then if the producer has finished n more products then the consumer has taken then the buffer is full and the producer is blocked. The next time the consumer takes a product V(full) is called, and the producer is allowed to make another product. The third is the empty semaphore. It is the mirror image of full. It starts at 0, and every time producer makes an item, it calls V(empty) indicating that there is another item on the buffer. Before taking another item the consumer calls P(empty) to makes sure there is an item on the buffer, and if there is to indicate that the consumer is taking it off. Therefore, if the buffer is empty, empty=0 and consumer is blocked until the producer produces another item and calls V(empty). If the buffer is full then full=0 and the producer is blocked until the consumer takes another item off the buffer and calls V(full). This producer/consumer problem can be built on like having multiple producers, consumers, buffer, and item conditions (like the number of items being produced) using more semaphores or variables. Example: As an example, if we had 2 consumers, and we wanted to produce 100 units of product, we need some way for the consumers to know when to stop. To do this we need to introduce a variable that keeps count of the how many units have been consumed between the two consumers, and another semaphore to protect that variable: