最近工作中遇到一个场景,进入一个C接口后,里面会执行回调,原来的库是同步线程,回调一切正常。最近库更新了,接口内会创建一个线程去并发做一些工作来提高效率和体验,回调函数会被两个线程同时调用。
最初napi层和arkts层代码没有改动,运行直接cppcrash,由于跨了线程,内存申请直接崩。
改成线程安全函数来调用,结果发现回调"没有执行"。
自己写了个示例,发现回调可以触发,但原工程的就是不行。经过不停的追加日志跟踪,发现线程安全函数是在接口return后才开始执行的,而原工程在接口结束后,会结束掉调用线程来回收资源,作为一个18年的C/C++程序员,真的有点想爆粗口。
查了一下资料,官方的资料并不多,只是给了简单调用示例,后来从部分文字中看出,线程安全函数是把回调放入队列来执行,并发立即执行,这也是为什么回调在接口return之后才执行,因为接口是队列中正在执行的任务。
没办法,改逻辑,把回调放到专用线程里,保正线程不会停止,原接口内的各个线程,都不在调用本线程内的回调。
改过之后,逻辑是对的,但仍然会崩,而且必崩在接口结束后。观察了napi层所有变量的生命周期,没问题!那问题就出现在底层库了。底层库回调时会传一个指针,而这个指针在接口结束前会被释放。PC端回调是同步的,所以不会有任何问题,释放时不会有任何地方去使用这个地址,但harmony不行,回调是异步的,指针释放时,部分回调操作不能保正完成,而且,底层库和napi层也无法用锁或者其它方式同步。
最后,解决办法是把底层库的指针定义为全局,不释放也不多次申请空间。好处是避免了线程同步问题,缺点是常占内存资源。
总之,harmony回调时需要注意以下几点:
1、回调函数和调用者需要属于相同的线程,否则要使用线程安全函数,也许还有其它方法但没有明确的资料作为证据,本人也没发现,暂且这样说。
2、确保所有回调结束前,回调函数所在线程存在,并且调用回调的线程被允许调用回调函数。
3、确保回调中使用的指针地址不会在回调结束前释放,以及回调函数参数的生命周期不会在回调结束前结束。