~/wiki

serial dispatch queue

---
title: Serial Dispatch Queue
category: concepts
created: 2026-12-22
updated: 2026-12-22
tags: [serial-dispatch-queue, concurrency, background-processing, thread-safety, dispatch-queue, sequential-processing, audio-processing, real-time-systems]
sources: [raw/conversations/2026-05-06-cursor-gosim-hack-efcdcc83.md]
confidence: high
---

# Serial Dispatch Queue

Concurrency primitive that executes tasks one at a time in FIFO order, guaranteeing sequential execution without the complexity of locks or atomic operations. Essential for processing streaming data where order matters and shared state must be protected.

## Core Characteristics

### Sequential Execution
- Tasks execute one at a time in submission order
- No concurrent access to shared state
- Eliminates race conditions naturally
- Maintains data consistency without explicit synchronization

### Queue Creation
```swift
// Create dedicated serial queue
let processingQueue = DispatchQueue(
    label: "com.app.audio-processing",
    qos: .userInteractive  // High priority for real-time processing
)

// Execute work sequentially
processingQueue.async {
    // Process audio chunk 1
}
processingQueue.async {
    // Process audio chunk 2 (waits for chunk 1)
}

Audio Processing Applications

Streaming Inference

Perfect for wake-word-detection pipelines where:

  • Audio chunks must be processed in temporal order
  • Model state (buffers, embeddings) must be updated sequentially
  • Real-time constraints require predictable execution

Implementation Pattern

class WakeWordInferenceWorker {
    private let processingQueue = DispatchQueue(
        label: "wake-word-inference", 
        qos: .userInteractive
    )
    
    func processAudioChunk(_ samples: [Float]) {
        processingQueue.async { [weak self] in
            self?.pipeline.processSamples(samples)
            // Sequential access to rolling buffers
            // No race conditions on model state
        }
    }
}

Performance Characteristics

Quality of Service

  • .userInteractive: For real-time audio processing
  • .userInitiated: For user-requested operations
  • .utility: For background processing
  • .background: For non-urgent tasks

Thread Management

  • System manages underlying thread pool
  • Queue may migrate between threads
  • No thread affinity guarantees
  • Automatic thread lifecycle management

Integration with Swift Concurrency

Actor Alternative

Serial queues provide similar guarantees to actors but with different semantics:

// Serial queue approach
class AudioProcessor {
    private let queue = DispatchQueue(label: "audio")
    private var buffer: [Float] = []
    
    func process(_ samples: [Float]) {
        queue.async {
            self.buffer.append(contentsOf: samples)
            // Safe: sequential access guaranteed
        }
    }
}

// Actor approach  
actor AudioProcessor {
    private var buffer: [Float] = []
    
    func process(_ samples: [Float]) {
        buffer.append(contentsOf: samples)
        // Safe: actor isolation guaranteed
    }
}

Bridging Contexts

// From actor to serial queue
actor MainProcessor {
    func handleAudioTap(_ buffer: AVAudioPCMBuffer) {
        let samples = extractSamples(buffer)
        // Dispatch to background queue
        backgroundQueue.async {
            processInBackground(samples)
        }
    }
}

Common Patterns

Worker Pattern

Dedicated queue for specific processing tasks:

  • Audio processing worker
  • Network request worker
  • File I/O worker
  • Database transaction worker

Producer-Consumer

class StreamingProcessor {
    private let queue = DispatchQueue(label: "streaming")
    private var pendingChunks: [AudioChunk] = []
    
    func enqueue(_ chunk: AudioChunk) {
        queue.async {
            self.pendingChunks.append(chunk)
            self.processNextIfReady()
        }
    }
    
    private func processNextIfReady() {
        // Called on queue - no synchronization needed
        guard !pendingChunks.isEmpty else { return }
        let chunk = pendingChunks.removeFirst()
        processChunk(chunk)
    }
}

Memory Management

Capture Semantics

  • Use [weak self] to avoid retain cycles
  • [unowned self] for guaranteed lifetime
  • Copy captured data if lifetime unclear

Resource Cleanup

class ResourceProcessor {
    private let queue = DispatchQueue(label: "resources")
    
    deinit {
        // Clean shutdown
        queue.sync {
            cleanupResources()
        }
    }
}

See also