<!--
{
  "documentType" : "article",
  "framework" : "AudioToolbox",
  "identifier" : "/documentation/AudioToolbox/adding-parallel-real-time-threads-to-audio-workgroups",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Adding Parallel Real-Time Threads to Audio Workgroups"
}
-->

# Adding Parallel Real-Time Threads to Audio Workgroups

Optimize the performance of real-time audio threads that run in sync with the I/O thread by adding them to the audio device workgroup.

## Discussion

If your app creates one or more auxiliary real-time threads that run in sync with the audio server I/O thread, join them to its audio workgroup. Doing so informs the system that these threads are rendering to the same deadline, which helps it optimize performance.

### Add Your Threads to the Audio Device Workgroup

Begin by retrieving the audio device’s workgroup, so you can join your threads to it. Retrieve it from the I/O Audio Unit (AUHAL or AURemoteIO) using either the [`kAudioOutputUnitProperty_OSWorkgroup`](/documentation/AudioToolbox/kAudioOutputUnitProperty_OSWorkgroup) property for AUv2, or the [`AUAudioUnit`](/documentation/AudioToolbox/AUAudioUnit) [`osWorkgroup`](/documentation/AudioToolbox/AUAudioUnit/osWorkgroup) property for AUv3. In macOS, you can also get it directly from the audio device by using the HAL API. Then, in your real-time thread, call <doc://com.apple.documentation/documentation/os/os_workgroup_join> to join it to the workgroup, and call <doc://com.apple.documentation/documentation/os/os_workgroup_leave> when you’ve finished your work, as shown in the example below.

```objc
#import <AudioToolbox/AudioToolbox.h>
 
// Get the workgroup from the AUHal.
os_workgroup_t _Nonnull workgroup = auHal.osWorkgroup;

// An opaque join token that the client needs to pass to join and leave a workgroup.
os_workgroup_join_token_s joinToken;

void realtimeThreadFunction(os_workgroup_t workgroup) {
    
    // Join this thread to the workgroup.
    const int result = os_workgroup_join(workgroup, &joinToken);
    if (result == 0) {
        // Success.
    }
    else if (result == EALREADY) {
        // The thread is already part of a workgroup that can't be
        // nested in the the specified workgroup.
        return;
    }
    else if (result == EINVAL) {
        // The workgroup has been canceled.
        return;
    }
    
    // Work cycle.
    while (threadShouldRun) {
        // Typically, wait on a semaphore.
        waitForWork();
        // The controlling thread signaled this thread to stop.
        if (threadShouldRun) {
            performCustomAudioDSPWorkUnit();
            // Typically, signal a semaphore.
            signalWorkComplete();
        }
    }
    
    // Before exiting the thread, leave the workgroup.
    os_workgroup_leave(workgroup, &joinToken);
}
```

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)