HarmonyOS鸿蒙Next中在worker线程文件中导出一个类会导致程序崩溃

HarmonyOS鸿蒙Next中在worker线程文件中导出一个类会导致程序崩溃 例如在以下用向导创建的worker线程文件中导出一个TestThrdMsg类:

import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';

export class TestThrdMsg
{
    m_Typ: string | undefined;
};

const workerPort: ThreadWorkerGlobalScope = worker.workerPort;

/**
 * Defines the event handler to be called when the worker thread receives a message sent by the host thread.
 * The event handler is executed in the worker thread.
 *
 * @param event message data
 */
workerPort.onmessage = ( event: MessageEvents ) => {
};

/**
 * Defines the event handler to be called when the worker receives a message that cannot be deserialized.
 * The event handler is executed in the worker thread.
 *
 * @param event message data
 */
workerPort.onmessageerror = ( event: MessageEvents ) => {
};

/**
 * Defines the event handler to be called when an exception occurs during worker execution.
 * The event handler is executed in the worker thread.
 *
 * @param event error message
 */
workerPort.onerror = ( event: ErrorEvent ) => {
};

在其他任意文件中写上以下代码:

import { TestThrdMsg } from 'ets/workers/TestThrd';

let g_TestThrdMsgPt: TestThrdMsg = new TestThrdMsg();

编译没有任何报错,但直接运行app,马上就崩溃了,hilog日志显示如下:

cke_24705.png

我只要删除这个导出类,就不会崩溃了,这是编译器BUG还是系统BUG?还是别的什么问题?


更多关于HarmonyOS鸿蒙Next中在worker线程文件中导出一个类会导致程序崩溃的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

更多关于HarmonyOS鸿蒙Next中在worker线程文件中导出一个类会导致程序崩溃的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


不知道为什么会有这么奇怪的规定。

在HarmonyOS Next中,worker线程文件中直接导出类会导致程序崩溃。这是因为worker线程与主线程隔离,不支持直接共享类定义。应通过postMessage传递序列化数据或使用Serializable接口处理对象。导出类会破坏线程安全机制,引发运行时错误。

在HarmonyOS Next的Worker线程中直接导出类并跨线程使用确实会导致崩溃,这是当前的设计限制。

核心原因:Worker线程与主线程是隔离的独立运行环境。

  1. 环境隔离:Worker线程拥有独立的ArkTS引擎实例和内存空间。你在Worker文件中定义的类(如TestThrdMsg)仅存在于该Worker的运行时环境中。
  2. 跨线程访问限制:当你从主线程(或其他线程)通过import语句尝试导入并实例化一个在Worker中定义的类时,实际上是在两个不同的引擎实例间进行跨边界操作。这违反了线程隔离原则,ArkTS运行时无法处理这种跨环境的类定义传递,导致非法内存访问并崩溃。
  3. 正确的通信方式:Worker与主线程之间只能通过postMessage传递可序列化的数据(如基本类型、普通对象、ArrayBuffer等),不能传递函数、类定义、或类的实例。线程间通信的数据会被序列化和反序列化。

解决方案:

将需要共享的数据结构定义为普通的数据类或接口,并放在一个公共的、能被主线程和Worker线程都访问到的文件中(例如一个独立的.ets文件)。

  • 创建公共类型文件 (例如 CommonTypes.ets):

    // CommonTypes.ets
    export interface TestThrdMsg {
        m_Typ?: string;
    }
    // 或者使用类,但确保不包含方法逻辑,仅作为数据载体
    export class TestThrdMsgData {
        m_Typ: string | undefined = undefined;
    }
    
  • 在Worker线程文件中使用:

    // TestThrd.ets (Worker文件)
    import { TestThrdMsgData } from '../common/CommonTypes';
    // ... workerPort.onmessage 等代码
    // 在onmessage中,你可以使用 TestThrdMsgData 来构造或解析数据
    workerPort.onmessage = (event: MessageEvents) => {
        // 假设收到主线程发来的数据
        const receivedData: TestThrdMsgData = event.data;
        // 处理数据...
        // 发送数据回主线程
        const response = new TestThrdMsgData();
        response.m_Typ = "processed";
        workerPort.postMessage(response);
    };
    
  • 在主线程文件中使用:

    // 主线程文件
    import { TestThrdMsgData } from '../common/CommonTypes';
    import { worker } from '@kit.ArkTS';
    
    let g_worker = new worker.ThreadWorker('ets/workers/TestThrd');
    // 发送数据到Worker
    let msgToSend = new TestThrdMsgData();
    msgToSend.m_Typ = "request";
    g_worker.postMessage(msgToSend);
    
    // 接收来自Worker的数据
    g_worker.onmessage = (event: MessageEvents) => {
        const dataFromWorker: TestThrdMsgData = event.data;
        // 使用数据...
    };
    

总结: 这不是编译器或系统的BUG,而是对线程安全模型和隔离机制的强制约束。你必须通过共享类型定义和消息传递机制来实现线程间数据交换,而不是直接跨线程共享类定义本身。

回到顶部