• Mindscape ๐Ÿ”ฅ
    • Playlist ๐ŸŽง
  • Algorithm

    • 1018๋ฒˆ: ์ฒด์ŠคํŒ ๋‹ค์‹œ ์น ํ•˜๊ธฐ
    • 1966๋ฒˆ: ํ”„๋ฆฐํ„ฐ ํ
    • Python ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ํŒ
    • C++ std::vector ์‚ฌ์šฉ๋ฒ• ์ •๋ฆฌ
    • Vim ์‚ฌ์šฉ ๋งค๋‰ด์–ผ
  • Ubuntu

    • ๋ฆฌ๋ˆ…์Šค ์šฐ๋ถ„ํˆฌ GRUB ํฐํŠธ ๋ณ€๊ฒฝ
    • ์šฐ๋ถ„ํˆฌ ์ด๋ฏธ์ง€ ๋น„๋””์˜ค ์ธ๋„ค์ผ(๋ฏธ๋ฆฌ๋ณด๊ธฐ) ์•ˆ ๋ณด์ž„ ๋ฌธ์ œ ํ•ด๊ฒฐ
    • Wine ํ™˜๊ฒฝ์—์„œ ์นด์นด์˜คํ†ก ์‹คํ–‰ ์‹œ explorer.exe ๋œจ์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๋ฒ•
    • ์šฐ๋ถ„ํˆฌ Wine ์นด์นด์˜คํ†ก ์‚ฌ์ง„ ์ด๋ฏธ์ง€ ์Šคํฌ๋ฆฐ์ƒท ๋ถ™์—ฌ๋„ฃ๊ธฐ
    • Wine ์นด์นด์˜คํ†ก ์ด๋ชจ์ง€ ๊นจ์ง ๋ฌธ์ œ ํ•ด๊ฒฐ
    • Ubuntu ์œˆ๋„์šฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋„๊ธฐ
  • Wellness

    • ์ฐจ์ „์žํ”ผ (Psyllium Husk)
    • ์—‘์ŠคํŠธ๋ผ ๋ฒ„์ง„ ์˜ฌ๋ฆฌ๋ธŒ์œ  (Extra Virgin Olive Oil)
    • ์ž๊ฐ€๋น„๊ฐ•์„ธ์ฒ™ (Nasal Irrigation)
    • QCY HT08 (MeloBuds Pro Plus)
    • ์ฝ˜์„œํƒ€ (Concerta)
    • ์ธ๋ฐ๋†€ (Inderal)
    • ์„คํŠธ๋ž„๋ฆฐ (Sertraline)
    • ๋ฉœ๋ผํ† ๋‹Œ (Melatonin)
    • ์น˜๊ฒฝ๋ถ€ ๋งˆ๋ชจ์ฆ
    • ๋ฐ”๋ฒจ ์Šค์ฟผํŠธ (Barbell Squat)
  • Humanities

    • Nordvik, Russia
    • North Sentinel Island
    • ๋กฑ๊ณ ๋กฑ๊ณ (Rongorongo)
    • ๋ฐ”๋กœํฌ ์Œ์•… (Baroque Music)
  • Design

    • ๊ตฌ๊ธ€์˜ ์•„์ด์ฝ˜ ๋Œ€๊ฐœํŽธ โ€” 6๋…„ ๋งŒ์˜ ์‹ค์ˆ˜ ์ธ์ •
    • ์ œ๋Ÿด๋“œ ์  ํƒ€ โ€” ๋Ÿญ์…”๋ฆฌ ์Šคํฌ์ธ  ์›Œ์น˜์˜ ์ฐฝ์‹œ์ž
    • ๋ฐ”์šฐํ•˜์šฐ์Šค โ€” ํ˜„๋Œ€ ๋””์ž์ธ์˜ ์›์ 
  • Brands

    • NOMOS Glashรผtte
    • Frรฉdรฉrique Constant
    • KZ (Knowledge Zenith)
    • ์—์ŠคํŠธ๋ผ (AESTURA)
    • JINHAO (้‡‘่ฑช)
    • Herman Miller
    • ๋ฐ์Šค์ปค (DESKER)
    • ๋ฌด์‹ ์‚ฌ ์Šคํƒ ๋‹ค๋“œ (Musinsa Standard)
  • Finance

    • ํ˜„๋Œ€์นด๋“œ ZERO โ€” Edition2 vs Edition3 ๋น„๊ต
    • ์‹ ํ•œ์นด๋“œ ์ฒ˜์Œ
    • S&P 500 ETF ํˆฌ์ž ๊ฐ€์ด๋“œ
    • ํŒŒํ‚นํ†ต์žฅ vs CMA ํ†ต์žฅ
    • ๋ฒ„ํฌ์…” ํ•ด์„œ์›จ์ด (Berkshire Hathaway)
    • ๋น„ํŠธ์ฝ”์ธ(Bitcoin)
  • Products

    • ์˜ค๋””์˜ค ์ธํ„ฐํŽ˜์ด์Šค (Audio Interface)
    • ์ฟ ๋ฃจํ† ๊ฐ€ (KURUTOGA)
    • CX31993 DAC ๋™๊ธ€
    • ํด๋ Œ์ง• ๋ฐ€ํฌ (Cleansing Milk)
    • ํ”ผ์ ฏ ํ† ์ด (Fidget Toy)
    • ThinkPad
  • Programming Languages

    • 8.0. Statement Level Control Structures
    • 8. Subprogram
    • 9. Implementing Subprogram
    • 10.1. Abstract Data Types and Encapsulation Constructs
    • 10.2. Support for Object Oriented Programming
    • 11. Concurrency
    • 12. FPL (1)
    • 13. FPL (2)
    • 14. Exception Handling and Event Handling
    • Final Exam

11. Concurrency

์ž‘์„ฑ 2026. 6. 12.ยท์ˆ˜์ • 2026. 6. 12.

Concurrency

What do we study in this chapter?

  • Introduction
  • Introduction to Subprogram-Level Concurrency
  • Semaphores
  • Monitors
  • Message Passing
  • Ada support for Concurrency
  • Java Threads
  • C# Threads
  • Concurrency in Functional Languages
  • Statement-Level Concurrency

Introduction

Concurrency can occur at four levels:

  • [1] Machine instruction level
  • CPU ๋‚ด๋ถ€์—์„œ ๋ช…๋ น์–ด ํŒŒ์ดํ”„๋ผ์ด๋‹, ๋ช…๋ น์–ด ๊ฐ„ ๋ณ‘๋ ฌ ์‹คํ–‰ ๋“ฑ ํ•˜๋“œ์›จ์–ด์ ์œผ๋กœ ๋™์‹œ์„ฑ์ด ๊ตฌํ˜„
  • ํ˜„๋Œ€ CPU๋Š” ์‚ฐ์ˆ  ์—ฐ์‚ฐ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ๋ช…๋ น์–ด๋ฅผ ๋™์‹œ์— ์ˆ˜ํ–‰ (ADD R1, R2, R3 ์™€ LOAD R4, 0(R5) ๊ฐ€ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌ)
  • [2] High-level language statement level
  • [Ex] How data should be distributed over multiple memories and which statements can be executed concurrently
  • ์†Œ์Šค ์ฝ”๋“œ ์ฐจ์›์—์„œ ๋…๋ฆฝ์ ์ธ ๋ฌธ์žฅ๋“ค์ด ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ
  • [3] Unit level (executing two or more subprogram units simultaneously)
  • ํ•จ์ˆ˜, ์„œ๋ธŒ๋ฃจํ‹ด, ๋ชจ๋“ˆ ๋“ฑ ํ”„๋กœ๊ทธ๋žจ ๋‹จ์œ„๋“ค์ด ๋…๋ฆฝ์ ์œผ๋กœ ๋™์‹œ์— ์‹คํ–‰
  • [4] Program level
  • ์„œ๋กœ ๋‹ค๋ฅธ ๋…๋ฆฝ์ ์ธ ํ”„๋กœ๊ทธ๋žจ์ด ๋™์‹œ์— ์‹คํ–‰๋จ. ์šด์˜์ฒด์ œ์—์„œ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์Šค์ผ€์ค„๋ง

The goal of developing concurrent software is to produce scalable and portable concurrent algorithms

  • The number of processors increases
  • The lifetime of hardware is relatively short
  • Frequent hardware upgrades require software to be portable across platforms

Multiprocessor Architectures

  • Late 1950s - one general-purpose processor and one or more special-purpose processors for input and output operations
  • Early 1960s - multiple complete processors, used for program-level concurrency
  • Job scheduler of the OS distributes job to processors
  • Mid-1960s - multiple partial processors, used for instruction-level concurrency

Single-Instruction Multiple-Data (SIMD) machines

  • The same instruction simultaneously, each on different data
  • No synchronization is required, called as vector processors
  • [์˜ˆ] ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์ ์šฉ, ํ–‰๋ ฌ ์—ฐ์‚ฐ
  • Multiple-Instruction Multiple-Data (MIMD) machines
  • Multiple processors operate independently but can be synchronized
  • Distributed and shared memory system
  • [์˜ˆ] ๋ณ‘๋ ฌ๋กœ ์›น ์š”์ฒญ ์ฒ˜๋ฆฌ
  • A primary focus of this chapter is shared memory MIMD machines (multiprocessors)

Reason why not evolved faster to make use of concurrent machines

  • Power of processors has continually increased
  • ์ดˆ๊ธฐ์—๋Š” ๊ตณ์ด ๋‹ค์ค‘ ํ”„๋กœ์„ธ์„œ(concurrent machines)๋ฅผ ์“ธ ํ•„์š”๊ฐ€ ์ ์—ˆ์Œ
  • Concurrent machines increase the speed of computation with two factors
  • Clock rates have become faster with each new generation of processors
  • Concurrency have been built into processor architectures
  • Many other large computing tasks are now run on machines with large numbers of relatively small processors

Categories of Concurrency

  • Physical concurrency - Multiple independent processors (multiple threads of control)
  • ๋ฉ€ํ‹ฐ์ฝ”์–ด CPU์—์„œ ๊ฐ ์ฝ”์–ด๊ฐ€ ๋…๋ฆฝ๋œ ์ž‘์—… ์ˆ˜ํ–‰ GPU์˜ ๋ณ‘๋ ฌ ์—ฐ์‚ฐ
  • Logical concurrency - The appearance of physical concurrency is presented by time sharing one processor (software can be designed as if there were multiple threads of control)
  • ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์„œ๋ฅผ ์‹œ๋ถ„ํ• (time-sharing) ํ•˜์—ฌ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•จ

Coroutines (quasi-concurrency) have a single thread of control

  • ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์•ˆ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์ œ์–ด ์ „ํ™˜
  • ์‹ค์ œ ๋ณ‘๋ ฌ์„ฑ ์—†์Œ, ํ•˜์ง€๋งŒ ๋…ผ๋ฆฌ์  ํ๋ฆ„์€ ๋ถ„๋ฆฌ๋จ
  • Python generator ํ•จ์ˆ˜ (yield ์‚ฌ์šฉ)
  • A thread of control in a program is the sequence of program points reached as control flows through the program
  • "Thread of control"์€ ํ”„๋กœ๊ทธ๋žจ ๋‚ด์—์„œ ๋ช…๋ น์–ด๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ œ์–ด ํ๋ฆ„์˜ ๊ฒฝ๋กœ๋ฅผ ์˜๋ฏธ
  • ์ฆ‰, ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋˜๋ฉด์„œ ์–ด๋–ค ๋ช…๋ น์–ด๋“ค์ด ์–ด๋–ค ์ˆœ์„œ๋กœ ์‹คํ–‰๋˜๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ๋ฆ„
  • ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์กด์žฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ๋…๋ฆฝ์ ์ธ ์ œ์–ด ํ๋ฆ„์„ ๊ฐ€์ง

Motivations for the Use of Concurrency

  • Multiprocessor computers capable of physical concurrency are now widely used
  • Even if a machine has just one processor, a program written to use concurrent execution can be faster than the same program written for nonconcurrent execution
  • I/O ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ โ†’ ์ „์ฒด ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • Involves a different way of designing software that can be very usefulโ€”many real-world situations involve concurrency
  • ์˜ˆ: ์—ฌ๋Ÿฌ ์„ผ์„œ, ์‚ฌ์šฉ์ž ์ž…๋ ฅ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋“ฑ
  • Many program applications are now spread over multiple machines, either locally or over a network
  • ๋ถ„์‚ฐ DB, ์ฑ„ํŒ… ์‹œ์Šคํ…œ, ์˜จ๋ผ์ธ ๊ฒŒ์ž„ ์„œ๋ฒ„

Introduction to Subprogram-Level Concurrency

A task or process or thread is a program unit that can be in concurrent execution with other program units

  • Tasks differ from ordinary subprograms in that:
  • A task may be implicitly started
  • Ordinary subprograms์€ ๋ช…์‹œ์  ํ˜ธ์ถœ
  • When a program unit starts the execution of a task, it is not necessarily suspended
  • Ordinary subprograms์€ ํ˜ธ์ถœ์ž๋Š” ์ค‘๋‹จ๋˜๊ณ , ์„œ๋ธŒ๋ฃจํ‹ด์ด ๋๋‚˜์•ผ ๊ณ„์†๋จ
  • When a taskโ€™s execution is completed, control may not return to the caller
  • Ordinary subprograms์€ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ์ž์—๊ฒŒ ์ œ์–ด๊ฐ€ ๋Œ์•„๊ฐ
  • Tasks usually work together
  • Task: OS๋‚˜ ์‹œ์Šคํ…œ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋‹ค๋ฃจ๋Š” ๋…ผ๋ฆฌ์  ์ž‘์—… ๋‹จ์œ„ (์˜ˆ: ์Šค์ผ€์ค„๋ง ๋Œ€์ƒ)
  • Process: ๋…๋ฆฝ์ ์ธ ์‹คํ–‰ ํ™˜๊ฒฝ. ์ž์ฒด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๊ฐ€์ง€๊ณ  ์‹คํ–‰๋จ. ๊ฐ๊ฐ์˜ ํ”„๋กœ์„ธ์Šค๋Š” ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— ๊ฐ„์„ญ ๋ถˆ๊ฐ€
  • Thread: ํ”„๋กœ์„ธ์Šค ๋‚ด๋ถ€์˜ ์‹คํ–‰ ํ๋ฆ„ ๋‹จ์œ„. ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๊ณต์œ ํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰ ๊ฐ€๋Šฅ

Two General Categories of Tasks

  • Heavyweight tasks execute in their own address space
  • Lightweight tasks all run in the same address space โ€“ more efficient
  • A task is disjoint if it does not communicate with or affect the execution of any other task in the program in any way
  • ์„œ๋กœ ํ†ต์‹ , ๊ณต์œ , ๊ฐ„์„ญ์ด ์ „ํ˜€ ์—†๋Š” task, ๋™์‹œ์„ฑ ์ œ์–ด๋‚˜ ๋™๊ธฐํ™” ๋ฌธ์ œ๋„ ์—†์Œ

Task Synchronization

  • A mechanism that controls the order in which tasks execute
  • ๊ณต์œ  ์ž์›(๋ฐ์ดํ„ฐ) ์— ์ ‘๊ทผํ•  ๋•Œ ์ถฉ๋Œ์ด๋‚˜ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๊ผญ ํ•„์š”ํ•จ
  • Two kinds of synchronization are required when tasks share data
  • Cooperation synchronization
  • Competition synchronization
  • Task communication is necessary for synchronization, provided by:
  • Shared nonlocal variables
  • ์ „์—ญ ๋ณ€์ˆ˜, ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด์ง€๋งŒ, ๊ฒฝ์Ÿ ์กฐ๊ฑด(race condition) ๋ฐœ์ƒ ๊ฐ€๋Šฅ
  • Parameters
  • task ๊ฐ„ ํ˜ธ์ถœ ์‹œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์ƒ๋Œ€์ ์œผ๋กœ ์ œํ•œ์ , ํ•˜์ง€๋งŒ ๊ฐ„๋‹จํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์— ์œ ์šฉ
  • Message passing
  • ๋ช…์‹œ์  ํ†ต์‹  ๋ฐฉ์‹ (send/receive, queue ๋“ฑ)
  • ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์—†์ด๋„ ๋™๊ธฐํ™” ๊ฐ€๋Šฅ

Kinds of synchronization

  • Cooperation: Task A must wait for task B to complete some specific activity before task A can continue its execution, e.g., the producer-consumer problem
  • ์ž‘์—… ํ๋ฆ„ ์ˆœ์„œ ๋ณด์žฅ (Task ๊ฐ„ ํ˜‘๋ ฅ), ์ž‘์—… ๊ฐ„ ์˜์กด์„ฑ ์กด์žฌ
  • ํ•œ task๊ฐ€ ๊ณ„์† ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด ๋‹ค๋ฅธ task์˜ ํŠน์ • ์ž‘์—… ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ ค์•ผ ํ•จ
  • Competition: Two or more tasks must use some resource that cannot be simultaneously used, e.g., a shared counter
  • ์ž์› ์ถฉ๋Œ ๋ฐฉ์ง€ (Task ๊ฐ„ ๊ฒฝ์Ÿ), ๊ณต์œ  ์ž์› ์กด์žฌ
  • ์—ฌ๋Ÿฌ task๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๊ณต์œ  ์ž์›์„ ์‚ฌ์šฉํ•  ๋•Œ ์ถฉ๋Œ์„ ๋ฐฉ์ง€
  • Competition is usually provided by mutually exclusive access

Need for Competition Synchronization

  • Task A: TOTAL = TOTAL + 1

  • Task B: TOTAL = 2 * TOTAL

    1. Fetch the value of TOTAL.
    1. Perform the arithmetic operation.
    1. Put the new value back in TOTAL.
  • Depending on order, there could be four different results

  • If task A completes its operation before task B begins, the value will be 8

  • If A puts its value back first, the value of TOTAL will be 6

  • If B puts its value back first, the value of TOTAL will be 4

  • If B completes its operation before task A begins, the value will be 7

  • A situation that leads to these problems is sometimes called a race condition

One general method for providing mutually exclusive access

  • Allow only a single task to possess shared resource at a time
  • Three methods of providing for mutually exclusive access to a shared resource
  • Semaphores: ์ •์ˆ˜๊ฐ’์œผ๋กœ ์ž์› ์ ‘๊ทผ ์ƒํƒœ๋ฅผ ์ œ์–ด (wait, signal)
  • Monitors: ๊ณ ๊ธ‰ ์–ธ์–ด ์ˆ˜์ค€์—์„œ ์ž์› ๋ณดํ˜ธ๋ฅผ ์ถ”์ƒํ™”ํ•œ ๊ตฌ์กฐ (์ž์ฒด์ ์œผ๋กœ lock ๋‚ด์žฅ)
  • Message passing: ๊ณต์œ  ์ž์›์„ ์“ฐ์ง€ ์•Š๊ณ , task ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌ
  • Message Passing ๋ฐฉ์‹์—์„œ๋Š” ์ž์›์„ ์ง์ ‘ ๊ณต์œ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ํ”„๋กœํ† ์ฝœ ์ž์ฒด๊ฐ€ ์ƒํ˜ธ ๋ฐฐ์ œ์˜ ์ˆ˜๋‹จ

Scheduler

  • Providing synchronization requires a mechanism for delaying task execution
  • Task execution control is maintained by a program called the scheduler, which maps task execution onto available processors
  • ์„ธ๋งˆํฌ์–ด, ๋ฝ, ๋ชจ๋‹ˆํ„ฐ ๋“ฑ์˜ ๋™๊ธฐํ™” ๊ตฌ์กฐ๋Š” task์˜ ์ง„ํ–‰ ์กฐ๊ฑด์„ ๊ฒฐ์ •ํ•˜๊ณ  ์Šค์ผ€์ค„๋Ÿฌ๋Š” ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ์‹คํ–‰์„ ์ œ์–ดํ•จ

Task Execution States

  • New - created but not yet started
  • Ready - ready to run but not currently running (no available processor) in task ready queue
  • Running
  • Blocked - has been running, but cannot now continue (usually waiting for some event to occur)
  • Dead - no longer active in any sense
  • How is a ready task chosen to move to the running state when the task currently running has become blocked or whose time slice has expired?

Liveness and Deadlock

  • Liveness is a characteristic that a program unit may or may not have
  • In sequential code, it means the unit will eventually complete its execution
  • ํ•จ์ˆ˜๋‚˜ ๋ฃจํ”„๊ฐ€ ์–ธ์  ๊ฐ€๋Š” ์ข…๋ฃŒ๋œ๋‹ค๋ฉด liveness ๋ณด์žฅ
  • In a concurrent environment, a task can easily lose its liveness
  • ์—ฌ๋Ÿฌ task๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋ฉด, ์Šค์ผ€์ค„๋ง/๋™๊ธฐํ™”/๊ฒฝ์Ÿ ๋ฌธ์ œ๋กœ ์ธํ•ด ์–ด๋–ค task๋Š” ์˜์›ํžˆ ์‹คํ–‰๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ โ†’ liveness ์ƒ์‹ค
  • If all tasks in a concurrent environment lose their liveness, it is called deadlock
  • Threat to the reliability of a program

Design Issues for Concurrency

  • Competition and cooperation synchronization*
  • Controlling task scheduling
  • How can an application influence task scheduling
  • How and when tasks start and end execution
  • How and when are tasks created
  • *The most important issue

Methods of providing synchronization

  • Semaphores
  • Monitors
  • Message Passing

Semaphores

Dijkstra - 1965

  • A semaphore is a method controlling access to shared data using integer variables controlled by two atomic functions as a solution to deadlock
  • Semaphores have only two operations, wait(๋˜๋Š” P) and release(๋˜๋Š” V)
  • Proberen ("์‹œ๋„ํ•˜๋‹ค")โ†’ ์ž์›์„ ์‹œ๋„ํ•ด์„œ ์–ป๋Š” ์—ฐ์‚ฐ โ†’ P(S)๋Š” S(์„ธ๋งˆํฌ์–ด ๊ฐ’)๋ฅผ ๊ฐ์†Œ์‹œํ‚ค๊ณ , 0๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋Œ€๊ธฐ (โ†’ ์ž์›์ด ์—†๋‹ค๋Š” ๋œป)
  • Verhogen ("์ฆ๊ฐ€์‹œํ‚ค๋‹ค")โ†’ ์ž์›์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด์„œ ๊ฐ’ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์—ฐ์‚ฐ โ†’ V(S)๋Š” S ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๊ณ , ๋Œ€๊ธฐ ์ค‘์ธ task๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•˜๋‚˜๋ฅผ ๊นจ์›€
  • Semaphores can be used to implement guards on the code that accesses shared data

A semaphore is a data structure consisting of a counter and a queue for storing task descriptors

  • Counter (์ •์ˆ˜): ์ž์›์ด ์–ผ๋งˆ๋‚˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ„
  • Queue (๋Œ€๊ธฐ์—ด): ์ž์›์„ ์š”์ฒญํ–ˆ์ง€๋งŒ ์ง€๊ธˆ์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” task๋“ค์„ ๋ณด๊ด€ โ†’ task๋Š” ์—ฌ๊ธฐ์„œ block ์ƒํƒœ๋กœ ๋Œ€๊ธฐ
  • A task descriptor is a data structure that stores all of the relevant information about the execution state of the task
  • Semaphores can be used to provide both competition and cooperation synchronization
  • wait() ์—ฐ์‚ฐ: Counter๊ฐ€ 0 ์ด์ƒ์ด๋ฉด ๊ฐ์†Œ์‹œํ‚ค๊ณ  ์ž์› ์ ‘๊ทผ ํ—ˆ์šฉ. 0์ด๋ฉด โ†’ Task Descriptor๋ฅผ Queue์— ๋„ฃ๊ณ  block
  • release() ์—ฐ์‚ฐ: Counter ์ฆ๊ฐ€. ๋Œ€๊ธฐ ์ค‘์ธ task๊ฐ€ ์žˆ์œผ๋ฉด โ†’ ํ•˜๋‚˜๋ฅผ Queue์—์„œ ๊บผ๋‚ด ready ์ƒํƒœ๋กœ ๋ณต๊ท€

Example: A shared buffer

  • Producer์™€ Consumer๊ฐ€ ๊ณต์œ  ๋ฒ„ํผ(buffer) ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ/์–ธ๋”ํ”Œ๋กœ์šฐ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋ ค๋Š” ๊ตฌ์กฐ
  • The buffer is implemented as an ADT with the operations DEPOSIT and FETCH as the only ways to access the buffer
  • Use two semaphores for cooperation: emptyspots and fullspots
  • emptyspots: ๋ฒ„ํผ์— ๋‚จ์€ ๋นˆ ๊ณต๊ฐ„ ์ˆ˜ ์ถ”์ 
  • fullspots: ๋ฒ„ํผ์— ์ฑ„์›Œ์ง„ ํ•ญ๋ชฉ ์ˆ˜ ์ถ”์ 
  • The semaphore counters are used to store the numbers of empty spots and full spots in the buffer (to prevent buffer underflow and overflow)

DEPOSIT must first check emptyspots to see if there is room in the buffer

  • If there is room, the counter of emptyspots is decremented and the value is inserted
  • If there is no room, the caller is stored in the queue of emptyspots
  • When DEPOSIT is finished, it must increment the counter of fullspots

FETCH must first check fullspots to see if there is a value

  • If there is a full spot, the counter of fullspots is decremented and the value is removed
  • If there are no values in the buffer, the caller must be placed in the queue of fullspots
  • When FETCH is finished, it increments the counter of emptyspots
  • The operations of FETCH and DEPOSIT on the semaphores are accomplished through two semaphore operations named wait and release

Wait and Release Operations

wait(aSemaphore)
if aSemaphoreโ€™s counter > 0 then
decrement aSemaphoreโ€™s counter
else
put the caller in aSemaphoreโ€™s queue
attempt to transfer control to a ready task
-- if the task ready queue is empty,
-- deadlock occurs
end
release(aSemaphore)
if aSemaphoreโ€™s queue is empty then
increment aSemaphoreโ€™s counter
else
put the calling task in the task ready queue
transfer control to a task from aSemaphoreโ€™s queue
end

Producer and Consumer Tasks (cooperation synchronization)

semaphore fullspots, emptyspots;
fullspots.count = 0;
emptyspots.count = BUFLEN;
task producer;
loop
-- produce VALUE โ€”
wait (emptyspots); {wait for space}
DEPOSIT(VALUE);
release(fullspots); {increase filled}
end loop;
end producer;
task consumer;
loop
wait (fullspots);{wait till not empty}}
FETCH(VALUE);
release(emptyspots); {increase empty}
-- consume VALUE โ€“-
end loop;
end consumer;

ํ•ญ๋ชฉ๋ถ„๋ฅ˜์„ค๋ช…์—ญํ• 
DEPOSIT()๋ฒ„ํผ ์—ฐ์‚ฐ(ADT)๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…์ƒ์‚ฐ์ž ์—ญํ•  (Producer)
FETCH()๋ฒ„ํผ ์—ฐ์‚ฐ(ADT)๋ฒ„ํผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋ƒ„์†Œ๋น„์ž ์—ญํ•  (Consumer)
wait()์„ธ๋งˆํฌ์–ด ์—ฐ์‚ฐ์„ธ๋งˆํฌ์–ด ๊ฐ’์„ 1 ๊ฐ์†Œ ๊ฐ’์ด 0๋ณด๋‹ค ์ž‘์œผ๋ฉด ํ˜„์žฌ task๋ฅผ block์ž์› ์ ‘๊ทผ ์ „ ํ™•์ธ ๋ฐ ๋Œ€๊ธฐ ์ œ์–ด
release()์„ธ๋งˆํฌ์–ด ์—ฐ์‚ฐ์„ธ๋งˆํฌ์–ด ๊ฐ’์„ 1 ์ฆ๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ธ task๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•˜๋‚˜๋ฅผ ๊นจ์›€์ž์› ๋ฐ˜ํ™˜ ๋ฐ ๋Œ€๊ธฐ ์ค‘ task ์žฌ์‹คํ–‰ ํ—ˆ์šฉ

Semaphores (+competition synchronization)

  • task๊ฐ€ ๋™์‹œ์— ๊ณต์œ  ์ž์›์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜
  • A third semaphore, named access, is used to control access (competition synchronization) -> ์ƒํ˜ธ ๋ฐฐ์ œ(Mutual Exclusion) ๋ฅผ ๋ณด์žฅ
  • The counter of access will only have the values 0 and 1
  • Such a semaphore is called a binary semaphore(mutex semaphore)
  • Note that wait and release must be atomic!

Producer and Consumer Tasks

semaphore access, fullspots, emptyspots;
access.count = 1; // ๊ณต์œ  ์ž์› ์ ‘๊ทผ ์ œ์–ด (Binary Semaphore)
fullstops.count = 0; // ํ˜„์žฌ ๋ฒ„ํผ์— ๋“ค์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ ์ˆ˜
emptyspots.count = BUFLEN; // ๋ฒ„ํผ์˜ ๋‚จ์€ ๊ณต๊ฐ„ ์ˆ˜
task producer;
loop
-- produce VALUE โ€“-
wait(emptyspots); {wait for space} //๊ณต๊ฐ„์ด ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (cooperation)
wait(access); {wait for access) //์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (competition)
DEPOSIT(VALUE); // buffer์— ์‚ฝ์ž…
release(access); {relinquish access} // ์ž์› ๋ฐ˜ํ™˜
release(fullspots); {increase filled} // ๋ฐ์ดํ„ฐ ํ•˜๋‚˜ ์ฑ„์›Œ์ง โ†’ Producer ๊นจ์›€
end loop;
end producer;
task consumer;
loop
wait(fullspots);{wait till not empty} // ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (cooperation)
wait(access); {wait for access} // ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ (competition)
FETCH(VALUE); // buffer์—์„œ ๋ฐ์ดํ„ฐ ๊บผ๋ƒ„
release(access); {relinquish access} // ์ž์› ๋ฐ˜ํ™˜
release(emptyspots); {increase empty} // ๊ณต๊ฐ„ ์ƒ๊น€ โ†’ Consumer ๊นจ์›€
-- consume VALUE โ€“-
end loop;
end consumer;

Evaluation of Semaphores

  • ์„ธ๋งˆํฌ์–ด๋Š” ๋งค์šฐ ์ทจ์•ฝํ•œ ๊ตฌ์กฐ โ†’ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์˜ ์‹ ๋ขฐ์„ฑ, ์•ˆ์ •์„ฑ, ์•ˆ์ „์„ฑ ๋ชจ๋‘ ์œ„ํ˜‘
  • Misuse of semaphores can cause failures in cooperation synchronization, e.g., the buffer will overflow if the wait of fullspots is left out
  • ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ โ†’ Consumer๊ฐ€ ์ฝ์„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Œ
  • Misuse of semaphores can cause failures in competition synchronization, e.g., the program will deadlock if the release of access is left out
  • Deadlock ๋ฐœ์ƒ โ†’ ๋‹ค์Œ task๊ฐ€ ์ž์›์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ฌดํ•œ ๋Œ€๊ธฐ
  • Unfortunately, ideal programmers are rare
  • ์„ธ๋งˆํฌ์–ด๋Š” ๋งค์šฐ ์ •๋ฐ€ํ•œ ์ œ์–ด๋ฅผ ์š”๊ตฌํ•˜๋ฉฐ, ์ž‘์€ ์‹ค์ˆ˜ ํ•˜๋‚˜๋กœ๋„ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ(๋ฐ๋“œ๋ฝ, ๊ฒฝ์Ÿ์กฐ๊ฑด, ๋ฌดํ•œ๋ฃจํ”„ ๋“ฑ) ๋ฐœ์ƒ ๊ฐ€๋Šฅ

Monitors

Ada, Java, C#

  • The idea: encapsulate the shared data and its operations to restrict access
  • A monitor is an abstract data type for shared data
  • Transferring responsibility for synchronization to the run-time system
  • ์ฆ‰, ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ wait()/release()์™€ ๊ฐ™์€ ์„ธ๋งˆํฌ์–ด๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š์•„๋„, ๋ชจ๋‹ˆํ„ฐ๊ฐ€ ๋™๊ธฐํ™” ์ฑ…์ž„์„ ๋Œ€์‹  ์ ธ์คŒ

Monitors (Competition Synchronization)

  • Shared data is resident in the monitor (rather than in the client units)
  • All access resident in the monitor
  • Monitor implementation guarantee synchronized access by allowing only one access at a time โ†’ ๊ฒฝ์Ÿ ์กฐ๊ฑด(race condition) ์„ ๋ฐฉ์ง€
  • Calls to monitor procedures are implicitly queued if the monitor is busy at the time of the call
  • ์ด๋ฏธ task๊ฐ€ ์‹คํ–‰ ์ค‘์ด๋ฉด, ๋‹ค๋ฅธ task๋Š” ์ž๋™์œผ๋กœ ๋Œ€๊ธฐ์—ด์— ์ €์žฅ๋จ
  • ๋ชจ๋“  ๊ณต์œ  ์ž์› ์ ‘๊ทผ์€ ๋ชจ๋‹ˆํ„ฐ ์•ˆ์—์„œ๋งŒ ์ด๋ฃจ์–ด์ง
  • ๋™์‹œ ์ ‘๊ทผ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์šด์˜์ฒด์ œ ๋˜๋Š” ๋Ÿฐํƒ€์ž„์ด ์ž๋™ ์ œ์–ด
  • ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” lock / release ๊ฐ™์€ ์„ธ๋ถ€ ์ œ์–ด๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š์Œ
class BufferMonitor {
private int[] buffer;
private int count;
public synchronized void deposit(int item) {
// ์ž์›์— ๋Œ€ํ•œ ๋‹จ์ผ ์ ‘๊ทผ ๋ณด์žฅ
}
public synchronized int fetch() {
// ๋‹ค๋ฅธ task๋Š” ์ž๋™ ๋Œ€๊ธฐ
}
}

  • synchronized ํ‚ค์›Œ๋“œ ๋•๋ถ„์— ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ thread๋งŒ ์ž…์žฅ ๊ฐ€๋Šฅ
  • ๋‹ค๋ฅธ thread๋Š” ์ž๋™์œผ๋กœ ํ์— ๋Œ€๊ธฐ๋จ
procedure producer(){
while (true){
item = produceItem();
ProducerConsumer.add(item);
}
}
monitor ProducerConsumer{
int itemCount = 0;
condition full;
condition empty;
procedure add(item){
if (itemCount == BUFFER_SIZE){
wait(full);
โ† ๋ฒ„ํผ๊ฐ€ ๊ฐ€๋“ ์ฐผ์œผ๋ฉด ๋Œ€๊ธฐ
}
putItemIntoBuffer(item);
itemCount = itemCount + 1;
if (itemCount == 1){
notify(empty
โ† ์†Œ๋น„์ž์—๊ฒŒ ๋ฒ„ํผ๊ฐ€ ๋น„์–ด ์žˆ์ง€ ์•Š์Œ์„ ์•Œ๋ฆผ
}
procedure consumer(){
while (true){
item = ProducerConsumer.remove();
consumeItem(item);
}
}
// ์ƒ์‚ฐ์ž๋Š” ํ•ญ์ƒ ์•„์ดํ…œ์„ ์ƒ์„ฑํ•˜๊ณ  add() ์‹œ๋„
// ์†Œ๋น„์ž๋Š” ํ•ญ์ƒ remove()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  consume
}
procedure remove(){
if (itemCount == 0){
wait(empty);
โ† ๋ฒ„ํผ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ๋Œ€๊ธฐ
}
item = removeItemFromBuffer();
itemCount = itemCount - 1;
if (itemCount == BUFFER_SIZE - 1){
notify(full);
โ† ์ƒ์‚ฐ์ž์—๊ฒŒ ๊ณต๊ฐ„์ด ์ƒ๊ฒผ์Œ์„ ์•Œ๋ฆผ
}
return item;
}

๊ตฌ์„ฑ์„ค๋ช…
ProducerConsumer๊ณต์œ  ๋ฒ„ํผ์™€ ๋™๊ธฐํ™” ๋กœ์ง์ด ํฌํ•จ๋œ ๋ชจ๋‹ˆํ„ฐ
itemCountํ˜„์žฌ ๋ฒ„ํผ์— ์ €์žฅ๋œ ์•„์ดํ…œ ์ˆ˜
full, empty์กฐ๊ฑด ๋ณ€์ˆ˜(condition) โ€” ๋ฒ„ํผ๊ฐ€ ๊ฐ€๋“ ์ฐผ๊ฑฐ๋‚˜ ๋น„์—ˆ์„ ๋•Œ ๋Œ€๊ธฐ
add(item)์ƒ์‚ฐ์ž๊ฐ€ ์•„์ดํ…œ์„ ๋ฒ„ํผ์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ชจ๋‹ˆํ„ฐ ์ ˆ์ฐจ
remove()์†Œ๋น„์ž๊ฐ€ ๋ฒ„ํผ์—์„œ ์•„์ดํ…œ์„ ๊บผ๋‚ด๋Š” ๋ชจ๋‹ˆํ„ฐ ์ ˆ์ฐจ
  • ๋ชจ๋‹ˆํ„ฐ ๋‚ด๋ถ€์— add()์™€ remove()๋ฅผ ์ •์˜ โ†’ ์ƒํ˜ธ ๋ฐฐ์ œ ์ž๋™ ๋ณด์žฅ
  • wait() ํ˜ธ์ถœ ์‹œ โ†’ ์ž๋™์œผ๋กœ ์กฐ๊ฑด ๋ณ€์ˆ˜ ํ์— ๋“ฑ๋ก๋จ (์–ธ์–ด/๋Ÿฐํƒ€์ž„์ด ์ฒ˜๋ฆฌ)

Monitors (Cooperation Synchronization)

  • Cooperation between processes is still a programming task
  • Programmer must guarantee that a shared buffer does not experience underflow or overflow
  • ๋ชจ๋‹ˆํ„ฐ๋Š” add()์™€ remove() ํ˜ธ์ถœ ์‹œ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ task๋งŒ ์ ‘๊ทผํ•˜๊ฒŒ ํ•จ โ†’ ์ƒํ˜ธ ๋ฐฐ์ œ๋Š” ์ž๋™ ๋ณด์žฅ
  • ํ•˜์ง€๋งŒ "๋ฒ„ํผ๊ฐ€ ๋น„์—ˆ์„ ๋•Œ ์†Œ๋น„์ž๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•˜๊ณ ", "๊ฐ€๋“ ์ฐผ์„ ๋•Œ ์ƒ์‚ฐ์ž๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•˜๋Š”" ๋กœ์ง์€ ์ง์ ‘ ์งœ์•ผ ํ•จ
  • ํ˜‘๋ ฅ์˜ ์ˆœ์„œ์™€ ์กฐ๊ฑด ํŒ๋‹จ์€ ์—ฌ์ „ํžˆ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ฝ”๋“œ๋กœ ๋ช…์‹œํ•ด์•ผ ํ•จ

Evaluation of Monitors

  • A better way to provide competition synchronization than are semaphores
  • ๋ฒ„๊ทธ ๋ฐœ์ƒ๋ฅ  ๊ฐ์†Œ (race condition, deadlock)
  • ์ฝ”๋“œ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ ์ฆ๊ฐ€
  • ์„ธ๋งˆํฌ์–ด๋ณด๋‹ค ์•ˆ์ „ํ•œ ๊ณ ์ˆ˜์ค€ ์ถ”์ƒํ™”
  • Semaphores can be used to implement monitors
  • Monitors can be used to implement semaphores
  • Support for cooperation synchronization is very similar as with semaphores, so it has the same problems

Message Passing

Message passing is a general model for concurrency

  • ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ  ๋ฐ›๋Š” ๊ฒƒ์ด ๋™๊ธฐํ™”(synchronization) ์˜ ์ˆ˜๋‹จ
  • It can model both semaphores and monitors
  • ์ž์› ์š”์ฒญ/ํ•ด์ œ๋ฅผ ๋ฉ”์‹œ์ง€๋กœ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
  • It is not just for competition synchronization
  • Central idea: task communication is like seeing a doctor
  • Most of the time she waits for you or you wait for her, but when you are both ready, you get together, or rendezvous

Message Passing Rendezvous

  • To support concurrent tasks with message passing, a language needs:
  • A mechanism to allow a task to indicate when it is willing to accept messages
  • A way to remember who is waiting to have its message accepted and some โ€œfairโ€ way of choosing the next message
  • When a sender taskโ€™s message is accepted by a receiver task, the actual message transmission is called a rendezvous

Ada Support for Concurrency

The Ada 83 Message-Passing Model

  • Ada tasks can be more active than monitors
  • Monitor: management services for the shared data
  • ์ˆ˜๋™์  ๊ด€๋ฆฌ์ž โ€“ ์™ธ๋ถ€ ํ˜ธ์ถœ์— ์‘๋‹ต, ๋‚ด๋ถ€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
  • Task: managers that can reside with the resource they manage
  • ๋Šฅ๋™์  ๊ด€๋ฆฌ์ž โ€“ ์ž์ฒด ๋กœ์ง ๋ณด์œ , ๋ฉ”์‹œ์ง€ ์ˆ˜์‹  ๋ฐ ์ฒ˜๋ฆฌ, entry point๋ฅผ ํ†ตํ•œ message passing
  • It has specification and body parts, like packages; the spec has the interface, which is the collection of entry points: (์–ด๋–ค ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ •์˜)
  • Entry points: locations where it can accept messages from other tasks
task Task_Example is
entry ENTRY_1 (Item : in Integer);
end Task_Example;

  • ๋‹ค๋ฅธ task๊ฐ€ ENTRY_1์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ  rendezvous ํ•  ์ˆ˜ ์žˆ์Œ

Task Body

  • The body task describes the action that takes place when a rendezvous occurs
  • entry point๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ ์–ด๋–ค ๋™์ž‘์„ ์ˆ˜ํ–‰ํ• ์ง€๋ฅผ ๋ช…์‹œ
  • A task that sends a message is suspended while waiting for the message to be accepted and during the rendezvous
  • ์ฆ‰, rendezvous๋Š” ์™„์ „ํ•œ ๋™๊ธฐ์  ํ†ต์‹  โ†’ ๋‘ task ๋ชจ๋‘ ์ค€๋น„๋˜์–ด์•ผ ์‹คํ–‰๋จ
  • Entry points in the spec are described with accept clauses in the body
accept entry_name (formal parameters) do
...
end entry_name;

Example of a Task Body

  • Skeletal body
task body Task_Example is
begin
loop -- task๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์ฒ˜๋ฆฌํ•จ
accept Entry_1 (Item: in Float) do
...
end Entry_1;
end loop;
end Task_Example;

  • ๋ฉ”์‹œ์ง€๊ฐ€ ๋„์ฐฉํ•ด์•ผ do ... end ๋ธ”๋ก์ด ์‹คํ–‰๋จ

Ada Message Passing Semantics

  • The task executes to the top of the accept clause and waits for a message
  • Task๋Š” accept ๋ฌธ๊นŒ์ง€ ์‹คํ–‰๋˜๊ณ , ํ•ด๋‹น entry point๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ฌ ๋•Œ๊นŒ์ง€ block ์ƒํƒœ๊ฐ€ ๋จ ์ฆ‰, receiver๋Š” ๋ฏธ๋ฆฌ ๋Œ€๊ธฐ
  • During execution of the accept clause, the sender is suspended
  • accept๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด: ์ง„์ž… ๋Œ€๊ธฐ (entry queue์— ์ €์žฅ)
  • accept๊ฐ€ ์ค€๋น„๋˜์—ˆ์„ ๊ฒฝ์šฐ: ๋‘˜์ด rendezvous๋ฅผ ์ˆ˜ํ–‰
  • accept parameters can transmit information in either or both directions
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์ž…๋ ฅ(in), ์ถœ๋ ฅ(out), ๋˜๋Š” ์ž…์ถœ๋ ฅ(in out) ์ „๋‹ฌ ๊ฐ€๋Šฅ
  • Every accept clause has an associated queue to store waiting messages
  • ๊ฐ entry๋Š” ์ž์ฒด์ ์ธ entry queue๋ฅผ ๊ฐ€์ง
  • ์—ฌ๋Ÿฌ task๊ฐ€ ๋™์‹œ์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ โ†’ ์ˆœ์„œ๋Œ€๋กœ ํ์— ์ €์žฅ
  • accept๊ฐ€ ์‹คํ–‰๋˜๋ฉด โ†’ ํ์—์„œ ํ•˜๋‚˜ ๊บผ๋‚ด์„œ ์ฒ˜๋ฆฌ

Rendezvous Time Lines

Message Passing: Server/Actor Tasks

  • A task that has accept clauses, but no other code is called a server task (the example above is a server task)
  • A task without accept clauses is called an actor task
  • An actor task can send messages to other tasks
  • Note: A sender must know the entry name of the receiver, but not vice versa (asymmetric)

Graphical Representation of a Rendezvous

  • Ada task that sends a message to another task must know the entry name in that task
  • A task entry need not know the name of the task from which it will accept messages
  • โ†’ Asymmetry
์†ก์‹ ์ž์ˆ˜์‹ ์ž
์ˆ˜์‹ ์ž์˜ ์ •ํ™•ํ•œ ์‹๋ณ„์ž(entry ํฌํ•จ) ํ•„์š”๋ฐœ์‹ ์ž์˜ ์ด๋ฆ„์ด๋‚˜ ์ˆ˜ ์•Œ ํ•„์š” ์—†์Œ
๋Šฅ๋™์ ์œผ๋กœ ํ˜ธ์ถœ์ˆ˜๋™์ ์œผ๋กœ ๋Œ€๊ธฐ
์ œ์–ด ํ๋ฆ„์„ ๋„˜๊ธด๋‹ค์ œ์–ด ํ๋ฆ„์„ ๋ฐ›๋Š”๋‹ค

Multiple Entry Points

  • Tasks can have more than one entry point
  • The specification task has an entry clause for each
  • The task body has an accept clause for each entry clause, placed in a select clause, which is in a loop
  • ๋™์‹œ ์š”์ฒญ์ด ์žˆ๋‹ค๋ฉด ์ˆœ์„œ/์ •์ฑ…์— ๋”ฐ๋ผ ์„ ํƒ

A Task with Multiple Entries

  • Following skeletal teller task
task body Teller is
loop
select
accept Drive_Up(formal params) do
...
end Drive_Up;
โ€ฆ
or
accept Walk_Up(formal params) do
...
end Walk_Up;
...
end select;
end loop;
end Teller;

  • The action of the select: examine the queues associated with the two accept clauses

Semantics of Tasks with Multiple accept Clauses

  • If exactly one entry queue is nonempty, choose a message from it
  • โ†’ ํ•ด๋‹น entry์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜๋ฝํ•˜๊ณ  rendezvous
  • If more than one entry queue is nonempty, choose one, nondeterministically, from which to accept a message
  • โ†’ ๋น„๊ฒฐ์ •์ (nondeterministic) ์œผ๋กœ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ด ์ˆ˜๋ฝ
  • If all are empty, wait โ†’ ๋ชจ๋“  entry๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ block ์ƒํƒœ ์œ ์ง€
  • The construct is often called a selective wait
  • Extended accept clause - code following the clause, but before the next clause
  • Executed concurrently with the caller
  • Sender์™€ Receiver๊ฐ€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๋™์•ˆ Sender๋Š” suspended ์ƒํƒœ๋กœ ๋Œ€๊ธฐ ์ค‘

Cooperation Synchronization with Message Passing

  • Provided by Guarded accept clauses
when not Full(Buffer) =>
accept Deposit (New_Value) do
...
end

  • An accept clause with a with a when clause is either open or closed
  • when ์ ˆ์„ ํ†ตํ•ด accept ๋ฌธ์ด ์—ด๋ฆด์ง€ ๋‹ซํž์ง€ ๊ฒฐ์ •๋จ(Guard๋Š” Boolean ์กฐ๊ฑด์‹)
  • A clause whose guard is true is called open
  • A clause whose guard is false is called closed
  • A clause without a guard is always open
  • ํ˜‘๋ ฅ ๋™๊ธฐํ™”(cooperation) โ†’ task๊ฐ€ ํŠน์ • ์ƒํƒœ์ผ ๋•Œ๋งŒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„๋“ค์ด๋„๋ก ํ•จ โ†’ ์˜ˆ: ๋ฒ„ํผ๊ฐ€ ๋น„์—ˆ์„ ๋•Œ๋Š” Read ์ˆ˜๋ฝ ๋ถˆ๊ฐ€, ๊ฝ‰ ์ฐผ์„ ๋•Œ๋Š” Write ์ˆ˜๋ฝ ๋ถˆ๊ฐ€

Semantics of select with Guarded accept Clauses:

  • select first checks the guards on all clauses
  • If exactly one is open, its queue is checked for messages
  • If more than one are open, non-deterministically choose a queue among them to check for messages
  • If all are closed, it is a runtime error
  • A select clause can include an else clause to avoid the error
  • When the else clause completes, the loop repeats

Competition Synchronization with Message Passing

  • Modeling mutually exclusive access to shared data
  • ๊ณต์œ  ์ž์›(์˜ˆ: ๋ฒ„ํผ) ์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ task๋งŒ ์ ‘๊ทผํ•˜๋„๋ก ์ œํ•œํ•˜๋Š” ๊ฒƒ
  • Example--a shared buffer
  • Encapsulate the buffer and its operations in a task
  • Competition synchronization is implicit in the semantics of accept clauses
  • Only one accept clause in a task can be active at any given time
  • ์ž๋™ ์ƒํ˜ธ ๋ฐฐ์ œโ†’Ada์˜ ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ ๋ชจ๋ธ ์ƒ, ํ•œ ์‹œ์ ์— ํ•˜๋‚˜์˜ accept ์ ˆ๋งŒ ํ™œ์„ฑํ™” ๊ฐ€๋Šฅ

Shared Buffer Code

  • Ada task implementation
task body Buf_Task is
Bufsize : constant Integer := 100;
Buf : array (1..Bufsize) of Integer;
Filled : Integer range 0..Bufsize := 0;
Next_In, Next_Out : Integer range 1..Bufsize := 1;
begin
loop
select
when Filled < Bufsize =>
accept Deposit(Item : in Integer) do
Buf(Next_In) := Item;
end Deposit;
Next_In := (Next_In mod Bufsize) + 1;
Filled := Filled + 1;
or
when Filled > 0 =>
accept Fetch(Item : out Integer) do
Item := Buf(Next_Out);
end Fetch;
Next_Out := (Next_Out mod Bufsize) + 1; Filled := Filled - 1;
โ€ฆ
end select
end loop;
end Buf_Task;

A Consumer Task

  • Ada task implementation
task Producer;
task body Producer is
New_Value : Integer;
begin
loop
-- produce New_Value --
Buf_Task.Deposit(New_Value);
end loop;
end Producer;
task Consumer;
task body Consumer is
Stored_Value : Integer;
begin
loop
Buf_Task.Fetch(Stored_Value);
-- consume Stored_Value โ€“
end loop;
end Consumer;

Concurrency in Ada 95

  • Ada 95 includes Ada 83 features for concurrency, plus two new features
  • Protected objects: A more efficient way of implementing shared data to allow access to a shared data structure to be done without rendezvous
  • ๊ณต์œ  ์ž์›์— ๋Œ€ํ•œ ๊ฒฝ์Ÿ ๋™๊ธฐํ™”(competition sync) ์„ ๋” ๋น ๋ฅด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๊ตฌํ˜„
  • ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›์ง€ ์•Š๊ณ , ๋‹จ์ˆœํžˆ ๋™๊ธฐํ™”๋œ ๊ณต์œ  ๋ฐ์ดํ„ฐ ์ ‘๊ทผ๋งŒ ์ œ๊ณต
  • ์–ธ์ œ๋“ ์ง€ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์ž๋™ ์ƒํ˜ธ ๋ฐฐ์ œ๊ฐ€ ์ ์šฉ๋จ
  • ์ž์› ๋ณดํ˜ธ ์ค‘์‹ฌ์˜ ์ƒํ˜ธ ๋ฐฐ์ œ
  • Asynchronous communication
  • ์ œํ•œ์ ์ด์—ˆ๋˜ ๋น„๋™๊ธฐ์  ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•ด์ง
  • task ๊ฐ„ rendezvous ์—†์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ฑฐ๋‚˜ ์ฒ˜๋ฆฌ ์ง€์—ฐ ์—†์ด ์‘๋‹ต ์ฒ˜๋ฆฌ
  • ์ˆ˜์‹ ์ž๊ฐ€ ๋‹น์žฅ ์ค€๋น„๋˜์ง€ ์•Š์•„๋„, ์ผ์ • ์‹œ๊ฐ„ ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜, ํŠน์ • ์กฐ๊ฑด์—์„œ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์—†์ด ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋จ โ†’ delay, requeue, abort

Ada 95: Protected Objects

  • A protected object is similar to an abstract data type
  • Access to a protected object is either through messages passed to entries, as with a task, or through protected subprograms
  • A protected procedure provides mutually exclusive read-write access to protected objects
  • A protected function provides concurrent read-only access to protected objects
protected Buffer is
entry Deposit(Item : in Integer);
entry Fetch(Item : out Integer);
private
Bufsize : constant Integer := 100;
Buf : array (1..Bufsize) of Integer;
Filled : Integer range 0..Bufsize := 0;
Next_In,
Next_Out : Integer range 1..Bufsize := 1;
end Buffer;
protected body Buffer is
entry Deposit(Item : in Integer)
when Filled < Bufsize is
begin
Buf(Next_In) := Item;
Next_In := (Next_In mod Bufsize) + 1;
Filled := Filled + 1;
end Deposit;
entry Fetch(Item : out Integer) when Filled > 0 is
begin
Item := Buf(Next_Out);
Next_Out := (Next_Out mod Bufsize) + 1;
Filled := Filled - 1;
end Fetch;
end Buffer;

Evaluation of the Ada

  • Message passing model of concurrency is powerful and general
  • Protected objects are a better way to provide synchronized shared data
  • In the absence of distributed processors, the choice between monitors and tasks with message passing is somewhat a matter of taste
  • ์„ค๊ณ„ ์Šคํƒ€์ผ์ด๋‚˜ ํŒ€์˜ ์„ ํ˜ธ์— ๋”ฐ๋ผ ์„ ํƒ ๊ฐ€๋Šฅ
  • ๋ชจ๋“ˆํ™”๋œ ์„œ๋น„์Šค โ†’ task + entry
  • ๋‹จ์ˆœ ์ž์› ๋ณดํ˜ธ โ†’ protected
  • For distributed systems, message passing is a better model for concurrency
  • ํ”„๋กœ์„ธ์„œ ๊ฐ„ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ์—์„œ โ†’ message passing์€ ์œ ์ผํ•˜๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ์„ ํƒ์ง€

Java Threads

The concurrent units in Java are methods named run

  • A run method code can be in concurrent execution with other such methods
  • The process in which the run methods execute is called a thread
  • threads are lightweight tasks
  • They all run in the same address space
  • extends Thread or implements Runnable
class myThread extends Thread{
public void run () {โ€ฆ}
}
โ€ฆ
Thread myTh = new MyThread ();
myTh.start();

Controlling Thread Execution

  • The Thread class has several methods to control the execution of threads
  • The run method describes the actions of the thread
  • The start method starts its thread as a concurrent unit by calling its run method
  • The yield is a request from the running thread to voluntarily surrender the processor (์งง์€ ์–‘๋ณด ๋˜๋Š” ์ปจํ…์ŠคํŠธ ์ „ํ™˜ ์œ ๋„์— ์‚ฌ์šฉ)
  • The sleep method can be used by the caller of the method to block the thread
  • The join method is used to force a method to delay its execution until the run method of another thread has completed its execution
  • ์„ ํ–‰ ์ž‘์—… ์™„๋ฃŒ ๋Œ€๊ธฐ์— ๋งค์šฐ ์œ ์šฉ
public void run() {
...
Thread myTh = new Thread();
myTh.start();
// do part of the computation of this thread
myTh.join(); // Wait for myTh to complete
// myTh.join(2000); // calling thread to wait two seconds for myTh to complete
// do the rest of the computation of this thread
}

Thread Priorities

  • A threadโ€™s default priority is the same as the thread that create it
  • If main creates a thread, its default priority is NORM_PRIORITY(5)
  • Threads defined two other priority constants, MAX_PRIORITY(10) and MIN_PRIORITY(1)
  • The priority of a thread can be changed with the methods setPriority
Thread t = new MyThread();
t.setPriority(Thread.MAX_PRIORITY); // ์šฐ์„ ์ˆœ์œ„ 10

Semaphores in Java

  • java.util.concurrent.Semaphore package defines the Semaphore class
  • Counting semaphores
  • acquire, release methods
  • deposit operation of the producer
fullspots = new Semaphore(0);
emptyspots = new Semaphore(BUFLEN);
// deposit operation of the producer
emptyspots.acquire(); // ๋ฒ„ํผ์— ๋นˆ ๊ณต๊ฐ„์ด ์žˆ๋Š”์ง€ ํ™•์ธ (์—†์œผ๋ฉด block)
deposit(value); // ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฒ„ํผ์— ์‚ฝ์ž…
fullspots.release(); // ์†Œ๋น„์ž์—๊ฒŒ ์•Œ๋ฆผ: ๋ฐ์ดํ„ฐ ํ•˜๋‚˜ ์ฑ„์›Œ์กŒ์Œ
// fetch operation of the consumer
fullspots.acquire(); // ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ (์—†์œผ๋ฉด block)
fetch(value); // ๋ฒ„ํผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋ƒ„
emptyspots.release(); // ์ƒ์‚ฐ์ž์—๊ฒŒ ์•Œ๋ฆผ: ๋นˆ ๊ณต๊ฐ„ ํ•˜๋‚˜ ์ƒ๊น€

Competition Synchronization with Java Threads

  • A method that includes the synchronized modifier disallows any other method from running on the object while it is in execution
public synchronized void deposit(int i) {โ€ฆ}
public synchronized int fetch() {โ€ฆ}

  • The above two methods are synchronized which prevents them from interfering with each other
  • If only a part of a method must be run without interference, it can be synchronized thru synchronized (expression) statement

Cooperation Synchronization with Java Threads

  • Cooperation synchronization in Java is achieved via wait, notify, and notifyAll methods
  • All methods are defined in Object, which is the root class in Java, so all objects inherit them
  • The wait method must be called in a loop
  • The notify method is called to tell one waiting thread that the event it was waiting has happened
  • The notifyAll method awakens all of the threads on the objectโ€™s wait list

Javaโ€™s Thread Evaluation

  • Javaโ€™s support for concurrency is relatively simple but effective
  • Not as powerful as Adaโ€™s tasks
  • No mechanism for communication, except through shared data

C# Threads

Loosely based on Java but there are significant differences

  • Basic thread operations
  • Any method can run in its own thread
  • A thread is created by creating a Thread object
  • Creating a thread does not start its concurrent execution; it must be requested through the Start method
public void MyRun1() { ... }
...
Thread myThread = new Thread(new ThreadStart(MyRun1));
myThread.Start();

  • A thread can be made to wait for another thread to finish with Join
  • A thread can be suspended with Sleep
  • A thread can be terminated with Abort

Synchronizing Threads

  • Three ways to synchronize C# threads
  • The Interlocked class
  • Used when the only operations that need to be synchronized are incrementing or decrementing of an integer (Interlocked.Increment(ref counter);)
  • ๊ฐ„๋‹จํ•œ ์ •์ˆ˜ ์—ฐ์‚ฐ(์ฆ๊ฐ€, ๊ฐ์†Œ, ๊ตํ™˜ ๋“ฑ)์„ ๊ฒฝ์Ÿ ์กฐ๊ฑด ์—†์ด atomicํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
  • The lock statement
  • Used to mark a critical section of code in a thread (lock (expression) {โ€ฆ })
  • ํŠน์ • ์ฝ”๋“œ ๋ธ”๋ก์„ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ thread๋งŒ ์‹คํ–‰ํ•˜๋„๋ก ๋ณดํ˜ธ
  • The Monitor class
  • Provides four methods that can be used to provide more sophisticated synchronization

C#โ€™s Concurrency Evaluation

  • An advance over Java threads, e.g., any method can run its own thread
  • Thread termination is cleaner than in Java
  • Synchronization is more sophisticated

Concurrency in Functional Languages

Multilisp (extension to Scheme) Concurrent ML (extension to ML) F#

Multilisp (extension to Scheme)

  • Extended with constructs for parallel computing execution and shared memory
  • involve side effects
  • Scheme (derived from LISP)
  • Properties: Simple syntax, dynamic typing, tail recursion optimization
  • s-expression (symbolic expression): a method representing any code or data
  • Expressed as a tree structure using a list
  • ((operator operand1 operand2 ...))
  • [EX] define f (lambda (x) โ€ฆ )a
  • fabcda
  • f: function, others: parameters
  • [EX] + 3 2
  • +: operator, 3,2: operands
  • '(a b c)
  • '(a (b c))

Multilisp (extension to Scheme) supports concurrency

  • pcall construct can be used
  • Allowed to be evaluated in parallel
  • [EX] (pcall f a b c d)
  • f: function, others: parameters
  • Parameters of the function can be evaluated concurrently
  • After waiting for all of these evaluations to return a value, the procedure value of the expression f is applied to the argument values resulting from evaluating a, b, c

future construct can be also used (more radical construct than pcall)

  • Function is evaluated in a separate thread
  • Syntax: (future <exp>)
  • Semantics:
  • Evaluate <exp> concurrently with calling program
  • Return reference to future immediately
  • Block if value of <exp> is required
  • [EX] (+ (future (fib (- x 1))) (future (fib (- x 2))))
  • Futures somewhat resemble Lazy Evaluation
  • Evaluation delayed until its value is needed but guaranteed

pcall

  • fork/join parallelism โ€“ ์—ฌ๋Ÿฌ ์ธ์ž๋ฅผ ๋ณ‘๋ ฌ๋กœ ํ‰๊ฐ€ ํ›„ ํ•˜๋‚˜์˜ ํ•จ์ˆ˜์— ์ ์šฉ
  • future
  • asynchronous thread
  • block on read
  • ๋น„๋™๊ธฐ thread ์ƒ์„ฑ โ€“ ํ‘œํ˜„์‹์„ ๋ณ„๋„ thread์—์„œ ํ‰๊ฐ€, ๊ฒฐ๊ณผ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ block

Concurrent ML (extension to ML)

  • Supports concurrency
  • ML (Meta Language)
  • General-purpose, high-level, functional programming language
  • Impure functional language (allow side-effects)
  • Type inference, static typing, static scoping rules ..
  • ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์—์„œ ๋ณ€์ˆ˜๋Š” ๊ฐ’์ด ๋ฐ”์ธ๋”ฉ๋œ ์ด๋ฆ„์ผ ๋ฟ, ๋ถˆ๋ณ€(immutable), ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋งŒ๋“ค๋ฉด ์ƒˆ๋กœ์šด ์ด๋ฆ„์ด ์ƒ๊น€
  • [EX] val distance = time * speed ; (* ๋ณ€์ˆ˜ ์„ ์–ธ (ํƒ€์ž… ์ถ”๋ก  ๊ฐ€๋Šฅ) *)
  • [EX]
let
val pi = 3.14159
in
pi * radius * radius
end ; (* ์ง€์—ญ ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ *)

  • [EX] fun square (x : int) = x * x ; (* ๋ช…์‹œ์  ํƒ€์ž… ํ•จ์ˆ˜ ์ •์˜ *)

Concurrent ML (extension to ML) supports concurrency

  • thread and synchronous message passing are supported
  • thread can be created with spawn
  • Takes function as parameter
  • thread is created โ†’ the function begins in the new thread
  • Channels provide the means of communicating between thread
  • Primary operations are for sending (send) and receiving (recv) messages
  • let val mychannel = channel()
  • send(mychannel, 7)
  • When more than one channel has received
  • guarded command do-od construct chooses randomly (Chapter 8)
structure Hello = struct
open CML
fun hello () = let
val c : string chan = channel ()
(* thread ๊ฐ„ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ์ฑ„๋„ ๊ฐ์ฒด ์ƒ์„ฑ *)
in
spawn (fn () => TextIO.print (recv c));
(* ์ƒˆ๋กœ์šด thread ์ƒ์„ฑ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ *)
send (c, "Hello, world!\n");
exit ()
end
fun main (_, argv) =
RunCML.doit (fn () => ignore (spawn hello), NONE)
end

F# support for concurrency based on the same .NET classes

  • Developed by Microsoft Research (MSR) based on OCaml, an ML family language
  • Conciseness
  • There is no need for braces, semicolons, etc
  • No need to specify object types (by powerful type inference)
  • Convenience
  • Functions can be passed as parameters
  • Reusable code by combining existing functions
  • Correctness
  • Strongly-typed
  • Values are basically immutable
  • Completeness
  • Object-oriented and procedural programming are also supported
  • Almost anything we can do in C# is possible
  • Can use all third-party .NET libraries and tools
  • Concurrency

F# support for concurrency based on the same .NET classes (i.e. System.Threading.Thread)

let createThread() =
let newThread = new Thread(myConMethod)
newThread.Start()

  • create the thread and start the execution of the function in the new thread
  • Shared immutable data does not require synchronization
  • For shared mutable data, locking will be required to prevent corruption of the shared data
  • lock function can be used
  • let sum = ref 0
  • lock(sum) (fun () -> sum := !sum + x)
  • First parameter: variable to be changed, second: lambda expression changing variable
  • mutable heap-allocated variable is of type ref
  • := assignment operator
  • exclamation point (!) to get its value

Statement-Level Concurrency

Objective: Provide a mechanism that the programmer can use to inform compiler of ways it can map the program onto multiprocessor architecture

  • ๋ณ‘๋ ฌ ์‹œ์Šคํ…œ์—์„œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณ‘๋ ฌ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ง์ ‘ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜

High-Performance Fortran

  • High-Performance Fortran (HPF; ACM, 1993b) is a collection of extensions to Fortran 90
  • A collection of extensions that allow the programmer to provide information to the compiler to help it optimize code for multiprocessor computers
  • Specify the number of processors, the distribution of data over the memories of those processors, and the alignment of data
  • HPF specification statements appear as special comments in a Fortran program !HPF$
  • It is comments in Fortran but can be recognized in HPF

Primary HPF Specifications

  • Number of processors
  • [EX] !HPF$ PROCESSORS procs (n)
  • Specify to the compiler the number of processors that can be used by the code generated for this program
  • procs๋Š” ๋…ผ๋ฆฌ์  ํ”„๋กœ์„ธ์„œ ์ง‘ํ•ฉ ์ด๋ฆ„
  • Distribution of data
  • [EX] !HPF$ DISTRIBUTE (kind) ONTO procs :: identifier_list
  • Specifies what data are to be distributed and the kind of distribution that is to be used
  • kind can be BLOCK (distribute data to processors in blocks) or CYCLIC (distribute data to processors one element at a time)
  • Relate the distribution of one array with that of another
  • [EX] ALIGN list1(index) WITH list2(index+1)
  • BLOCK: ๋ฐฐ์—ด์„ ์ผ์ •ํ•œ ๋ฉ์–ด๋ฆฌ๋กœ ๋‚˜๋ˆ„์–ด ๊ฐ ํ”„๋กœ์„ธ์„œ์— ๋ถ„๋ฐฐ
  • CYCLIC: ํ•˜๋‚˜์”ฉ ๋ฒˆ๊ฐˆ์•„ ๋ถ„๋ฐฐ (load balancing์— ์œ ๋ฆฌ)
  • list1์™€ list2๊ฐ€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ถ„ํฌ๋˜๋„๋ก ์ •๋ ฌ (ex. ์ธ์ ‘ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ตœ์ ํ™”)

Statement-Level Concurrency Example

  • [EX] In each execution of these assignment statements, the two referenced array elements will be stored in the memory of the same processor
REAL list_1 (1000), list_2 (1000)
INTEGER list_3 (500), list_4 (501)
!HPF$ PROCESSORS proc (10)
!HPF$ DISTRIBUTE (BLOCK) ONTO procs :: list_1, list_2
!HPF$ ALIGN list_3 (index) WITH list_4 (index+1)
...
list_1 (index) = list_2 (index)
list_3 (index) = list_4 (index+1)

  • ์‹ค์ˆ˜ํ˜• ๋ฐฐ์—ด 2๊ฐœ (list_1, list_2) ์™€ ์ •์ˆ˜ํ˜• ๋ฐฐ์—ด 2๊ฐœ (list_3, list_4) ์„ ์–ธ
  • ๋ณ‘๋ ฌ ์‹คํ–‰์„ ์œ„ํ•ด ๋…ผ๋ฆฌ์  ํ”„๋กœ์„ธ์„œ 10๊ฐœ๋ฅผ ์„ ์–ธ (์ด๋ฆ„: proc) ์ดํ›„์˜ ๋ถ„์‚ฐ(distribute)์€ ์ด 10๊ฐœ์˜ processor๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ด๋ฃจ์–ด์ง
  • list_1, list_2 ๋ฐฐ์—ด์„ 10๊ฐœ์˜ ํ”„๋กœ์„ธ์„œ์— BLOCK ๋ฐฉ์‹์œผ๋กœ ๋‚˜๋ˆ ์„œ ๋ถ„์‚ฐ
  • BLOCK ๋ถ„์‚ฐ: ๊ฐ ํ”„๋กœ์„ธ์„œ๊ฐ€ ์—ฐ์†๋œ ๋ธ”๋ก ๋‹จ์œ„๋กœ ์ผ๋ถ€ ์š”์†Œ๋ฅผ ์†Œ์œ 
  • list_3(index)๊ฐ€ list_4(index+1)๊ณผ ๊ฐ™์€ ํ”„๋กœ์„ธ์„œ์— ์œ„์น˜ํ•˜๋„๋ก ์ •๋ ฌ โ†’ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์ด ๋ณ‘๋ ฌํ™” ๋ฐ locality ์ตœ์ ํ™”์— ์œ ๋ฆฌํ•จ ์ด๋Š” list_3์˜ ์ธ๋ฑ์Šค๊ฐ€ list_4๋ณด๋‹ค 1๋งŒํผ ์ž‘๊ฒŒ ๋Œ€์‘๋จ์„ ์˜๋ฏธ
  • list_1๊ณผ list_2๋Š” BLOCK์œผ๋กœ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ถ„ํฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • list_3๊ณผ list_4(index+1)๋Š” ์ •๋ ฌ ์กฐ๊ฑด์ด ๋ช…์‹œ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์‹œ์—๋„ ์•ˆ์ „

FORALL statement specifies a sequence of assignment statements that may be executed concurrently

  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ• ๋‹น(assignment) ์„ ๋ณ‘๋ ฌ๋กœ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ช…์‹œํ•˜๋Š” ๊ตฌ๋ฌธ
  • Right side of all 1,000 assignments must be evaluated first, before any assignments take place โ†’ permits concurrent execution of all of the assignment statements
  • ์ด๋Š” ๋ณ‘๋ ฌ ๋ฃจํ”„์˜ ์•ˆ์ „์„ฑ(safety)์„ ํ™•๋ณด

Summary

Concurrent execution can be at the instruction, statement, or subprogram level

  • Physical concurrency: when multiple processors are used to execute concurrent units
  • Logical concurrency: concurrent united are executed on a single processor
  • Two primary facilities to support subprogram concurrency: competition synchronization and cooperation synchronization
  • Mechanisms: semaphores, monitors, rendezvous, threads
  • High-Performance Fortran provides statements for specifying how data is to be distributed over the memory units connected to multiple processors
์ตœ๊ทผ ์ˆ˜์ •: 26. 6. 12. ์˜คํ›„ 3:28
Contributors: kmbzn, Claude Sonnet 4.6
Prev
10.2. Support for Object Oriented Programming
Next
12. FPL (1)

BUILT WITH

CloudflareNode.jsGitHubGitVue.jsJavaScriptVSCodenpm

All trademarks and logos are property of their respective owners.
ยฉ 2026 kmbzn ยท MIT License