DEV Community

HarmonyOS
HarmonyOS

Posted on

Code Execution Restrictions in Worker Threads

Read the original article:Code Execution Restrictions in Worker Threads

Requirement Description

Move a time-consuming task (JSON parsing, HTTP requests, dbStore operations, eventHub posting, preferences, @Observer, and singleton access) from the main thread to a worker thread and ensure it executes normally without interruption or UI lag.

Background Knowledge

  • HarmonyOS worker threads have restrictions compared to the main thread. Not all APIs and libraries are thread-safe.
  • Cross-thread singletons must follow shared module rules ("use shared") and classes should be marked with @Sendable.
  • Certain operations (database, context-dependent APIs, UI modification) cannot directly run in worker threads.
  • Locks such as AsyncLock are necessary to protect shared data consistency.

Implementation Steps

  1. Identify which APIs are not supported in worker threads (e.g., dbStore, preferences, UI modification).
  2. Move heavy computation (parsing, calculations) into a worker thread, while keeping UI and restricted operations on the main thread.
  3. For shared data (e.g., singleton), create a shared module and mark classes as @Sendable.
  4. Use ArkTSUtils.locks.AsyncLock to protect concurrent modifications.
  5. Pass context or necessary references from the main thread to the worker thread when required.

Code Snippet / Configuration

Below is the demo shared for a @Sendable singleton ensuring uniqueness across threads:

import { ArkTSUtils , collections } from '@kit.ArkTS';

// Declare that the current module is a shared module and can only export Sendable data
"use shared"

// Shared module, TestData is globally unique
@Sendable
export class TestData {
  private keyHandle: string = '0';
  private count_: number = 0;
  private arr: collections.Array<number> = new collections.Array<number>();
  private isLoadingShowing: boolean = false;
  lock_: ArkTSUtils.locks.AsyncLock = new ArkTSUtils.locks.AsyncLock()
  private static instance: TestData;

  private constructor() {}

  public static getInstance(): TestData {
    if (TestData.instance == null) {
      TestData.instance = new TestData();
      console.debug('getInstance new')
    }
    return TestData.instance;
  }

  getLoadingShowing(): boolean {
    return this.isLoadingShowing;
  }

  setLoadingShowing(isShow: boolean) {
    this.isLoadingShowing = isShow
  }

  async getKeyHandle(): Promise<string> {
    return this.lock_.lockAsync(() => {
      return this.keyHandle;
    })
  }

  async setKeyHandle(keyHandle: string) {
    await this.lock_.lockAsync(() => {
      this.keyHandle = keyHandle;
    })
  }

  public async getCount(): Promise<number> {
    return this.lock_.lockAsync(() => {
      return this.count_;
    })
  }

  public async increaseCount() {
    await this.lock_.lockAsync(() => {
      this.count_++;
    })
  }

  public async getArr(): Promise<collections.Array<number>> {
    return this.lock_.lockAsync(() => {
      return this.arr;
    })
  }

  public async setArr(arr: collections.Array<number>) {
    return this.lock_.lockAsync(() => {
      this.arr = arr;
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

  • Singleton instance (TestData.getInstance()) works correctly across multiple threads.
  • State changes (count_, arr, keyHandle) are thread-safe using AsyncLock.
  • Heavy computation can be executed in the worker thread without blocking the UI.

Limitations or Considerations

  • Some APIs (e.g., dbStore, preferences, eventHub, UI updates) cannot be directly executed in worker threads. These must remain on the main thread or be proxied.
  • @Sendable and "use shared" must be applied to classes/modules to guarantee thread safety.
  • Context must be explicitly passed to worker threads from the main thread.
  • Code refactoring may be required if many main-thread-only APIs are mixed inside worker logic.

Written by Arif Emre Ankara

Top comments (0)