parallel.sh

Download
bash 294 lines 7.8 KB
  1#!/usr/bin/env bash
  2set -euo pipefail
  3
  4# Parallel Execution Patterns
  5# Demonstrates running multiple tasks concurrently with controlled parallelism
  6
  7# ============================================================================
  8# Color definitions for output
  9# ============================================================================
 10
 11readonly RED='\033[0;31m'
 12readonly GREEN='\033[0;32m'
 13readonly YELLOW='\033[1;33m'
 14readonly BLUE='\033[0;34m'
 15readonly CYAN='\033[0;36m'
 16readonly NC='\033[0m' # No Color
 17
 18# ============================================================================
 19# Parallel Execution Functions
 20# ============================================================================
 21
 22# Run commands in parallel with concurrency limit
 23# Usage: run_parallel <max_jobs> <command1> <command2> ...
 24run_parallel() {
 25    local max_jobs="$1"
 26    shift
 27    local -a commands=("$@")
 28
 29    echo -e "${CYAN}Running ${#commands[@]} commands with max $max_jobs parallel jobs${NC}"
 30
 31    local -a pids=()
 32    local job_count=0
 33
 34    for cmd in "${commands[@]}"; do
 35        # Wait if we've reached the concurrency limit
 36        while [[ ${#pids[@]} -ge $max_jobs ]]; do
 37            wait_for_any_pid pids
 38        done
 39
 40        # Launch command in background
 41        (
 42            eval "$cmd"
 43        ) &
 44
 45        local pid=$!
 46        pids+=("$pid")
 47        ((job_count++))
 48
 49        echo -e "  ${GREEN}${NC} Started job $job_count (PID $pid): $cmd"
 50    done
 51
 52    # Wait for all remaining jobs
 53    echo -e "${CYAN}Waiting for remaining jobs to complete...${NC}"
 54    for pid in "${pids[@]}"; do
 55        wait "$pid" 2>/dev/null || true
 56    done
 57
 58    echo -e "${GREEN}${NC} All jobs completed"
 59}
 60
 61# Wait for any PID in the array to finish, then remove it
 62wait_for_any_pid() {
 63    local -n pid_array=$1
 64
 65    # Wait for any child process
 66    wait -n 2>/dev/null || true
 67
 68    # Remove finished PIDs from array
 69    local -a still_running=()
 70    for pid in "${pid_array[@]}"; do
 71        if kill -0 "$pid" 2>/dev/null; then
 72            still_running+=("$pid")
 73        fi
 74    done
 75
 76    pid_array=("${still_running[@]}")
 77}
 78
 79# Wait for specific PIDs and collect their exit codes
 80# Returns: associative array of PID -> exit code
 81wait_for_pids() {
 82    local -a pids=("$@")
 83
 84    echo -e "${CYAN}Waiting for ${#pids[@]} process(es)...${NC}"
 85
 86    local -A exit_codes=()
 87
 88    for pid in "${pids[@]}"; do
 89        local exit_code=0
 90        wait "$pid" || exit_code=$?
 91
 92        exit_codes[$pid]=$exit_code
 93
 94        if [[ $exit_code -eq 0 ]]; then
 95            echo -e "  ${GREEN}${NC} PID $pid completed successfully"
 96        else
 97            echo -e "  ${RED}${NC} PID $pid failed with exit code $exit_code"
 98        fi
 99    done
100
101    # Print summary
102    local failed_count=0
103    for exit_code in "${exit_codes[@]}"; do
104        [[ $exit_code -ne 0 ]] && ((failed_count++))
105    done
106
107    if [[ $failed_count -eq 0 ]]; then
108        echo -e "${GREEN}${NC} All processes completed successfully"
109    else
110        echo -e "${YELLOW}${NC} $failed_count process(es) failed"
111    fi
112
113    return $failed_count
114}
115
116# Simulate a download task
117simulate_download() {
118    local file_id="$1"
119    local duration="$2"
120
121    echo -e "  ${BLUE}[File $file_id]${NC} Starting download..."
122    sleep "$duration"
123
124    # Simulate occasional failures
125    if [[ $((RANDOM % 10)) -eq 0 ]]; then
126        echo -e "  ${RED}[File $file_id]${NC} Download failed!"
127        return 1
128    fi
129
130    echo -e "  ${GREEN}[File $file_id]${NC} Download complete (${duration}s)"
131    return 0
132}
133
134# Parallel download demonstration
135parallel_download() {
136    local max_parallel="$1"
137    local -a files=("${@:2}")
138
139    echo -e "${CYAN}Downloading ${#files[@]} files with max $max_parallel parallel downloads${NC}\n"
140
141    local -a pids=()
142    local start_time=$SECONDS
143
144    for file_id in "${files[@]}"; do
145        # Limit concurrency
146        while [[ ${#pids[@]} -ge $max_parallel ]]; do
147            wait_for_any_pid pids
148        done
149
150        # Random duration between 1-3 seconds
151        local duration=$((1 + RANDOM % 3))
152
153        simulate_download "$file_id" "$duration" &
154        pids+=($!)
155    done
156
157    # Wait for remaining downloads
158    for pid in "${pids[@]}"; do
159        wait "$pid" 2>/dev/null || true
160    done
161
162    local elapsed=$((SECONDS - start_time))
163    echo -e "\n${GREEN}${NC} All downloads completed in ${elapsed}s"
164}
165
166# ============================================================================
167# Demo Section
168# ============================================================================
169
170demo_sequential_vs_parallel() {
171    echo -e "\n${BLUE}=== Sequential vs Parallel Execution ===${NC}\n"
172
173    # Define test tasks
174    local -a tasks=(
175        "sleep 1 && echo 'Task 1 done'"
176        "sleep 1 && echo 'Task 2 done'"
177        "sleep 1 && echo 'Task 3 done'"
178        "sleep 1 && echo 'Task 4 done'"
179    )
180
181    # Sequential execution
182    echo -e "${YELLOW}Sequential execution:${NC}"
183    local start=$SECONDS
184    for task in "${tasks[@]}"; do
185        eval "$task"
186    done
187    local seq_time=$((SECONDS - start))
188    echo -e "Time: ${seq_time}s\n"
189
190    # Parallel execution
191    echo -e "${YELLOW}Parallel execution (max 4 jobs):${NC}"
192    start=$SECONDS
193    run_parallel 4 "${tasks[@]}"
194    local par_time=$((SECONDS - start))
195    echo -e "Time: ${par_time}s\n"
196
197    echo -e "${GREEN}Speedup:${NC} ~$((seq_time / par_time))x faster"
198}
199
200demo_parallel_with_limit() {
201    echo -e "\n${BLUE}=== Parallel Execution with Concurrency Limit ===${NC}\n"
202
203    local -a tasks=()
204    for i in {1..8}; do
205        tasks+=("sleep 0.5 && echo 'Task $i completed'")
206    done
207
208    echo -e "${YELLOW}Running 8 tasks with max 3 parallel jobs:${NC}"
209    run_parallel 3 "${tasks[@]}"
210}
211
212demo_pid_tracking() {
213    echo -e "\n${BLUE}=== PID Tracking and Exit Code Collection ===${NC}\n"
214
215    # Start several background processes
216    sleep 0.5 && exit 0 &
217    local pid1=$!
218
219    sleep 0.5 && exit 1 &
220    local pid2=$!
221
222    sleep 0.5 && exit 0 &
223    local pid3=$!
224
225    sleep 0.5 && exit 2 &
226    local pid4=$!
227
228    echo "Started 4 background processes: $pid1, $pid2, $pid3, $pid4"
229    echo
230
231    wait_for_pids "$pid1" "$pid2" "$pid3" "$pid4"
232}
233
234demo_parallel_downloads() {
235    echo -e "\n${BLUE}=== Parallel Downloads Simulation ===${NC}\n"
236
237    # Test with different concurrency levels
238    local -a files=(1 2 3 4 5 6 7 8)
239
240    echo -e "${YELLOW}Test 1: Sequential (1 at a time)${NC}"
241    parallel_download 1 "${files[@]}"
242
243    echo -e "\n${YELLOW}Test 2: Parallel (4 at a time)${NC}"
244    parallel_download 4 "${files[@]}"
245}
246
247demo_job_control() {
248    echo -e "\n${BLUE}=== Job Control Example ===${NC}\n"
249
250    echo "Starting 5 background jobs..."
251
252    local -a pids=()
253    for i in {1..5}; do
254        (
255            sleep $((1 + RANDOM % 3))
256            echo "Job $i finished"
257        ) &
258        pids+=($!)
259        echo -e "  Started job $i (PID ${pids[-1]})"
260    done
261
262    echo
263    echo "Active background jobs:"
264    jobs -l
265
266    echo
267    echo "Waiting for all jobs to complete..."
268    for pid in "${pids[@]}"; do
269        wait "$pid"
270    done
271
272    echo -e "${GREEN}${NC} All jobs completed"
273}
274
275# ============================================================================
276# Main Execution
277# ============================================================================
278
279main() {
280    echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
281    echo -e "${BLUE}║      Parallel Execution Demo              ║${NC}"
282    echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
283
284    demo_sequential_vs_parallel
285    demo_parallel_with_limit
286    demo_pid_tracking
287    demo_parallel_downloads
288    demo_job_control
289
290    echo -e "\n${GREEN}=== All Parallel Execution Demos Complete ===${NC}\n"
291}
292
293main "$@"