You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

5个文本识别器并行运行的代码问题及支持性确认

问题

希望同时运行5个文本识别器实现并行处理,但日志显示识别器实际为串行执行。以下是实现代码与运行日志:

实现代码

class LocalOCRTestActivity : BaseActivity() {

    companion object {
        const val TAG = "LocalOCRTestActivity"
    }

    override fun layoutId(): Int = R.layout.activity_local_ocr_test

    private val executor = ThreadPoolExecutor(5, 10, 10, java.util.concurrent.TimeUnit.SECONDS, LinkedBlockingQueue(20))

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val imgPath = intent.getStringExtra(Constants.KEY_QUESTION_LOCAL_PIC) ?: ""
        ivOcr.setImageURI(Uri.fromFile(File(imgPath)))

        val image = InputImage.fromFilePath(this, Uri.fromFile(File(imgPath)))

        lifecycleScope.safeLaunch(Dispatchers.IO, exceptionHandler = { exception ->
            LogDelegate.d(TAG, "exception: ${exception.message}")
        }) {
            LogDelegate.d(TAG, "call recognizer")

            val LatinRecognizer = async { performTextRecognition(image) }
            val ChineseRecognizer = async { performChineseTextRecognition(image) }
            val DevanagariRecongizer = async { performDevanagariTextRecognition(image) }
            val JapaneseRecognizer = async { performJapaneseTextRecognition(image) }
            val KoreanRecognizer = async { performKoreanTextRecognition(image) }

            val KoreanText = KoreanRecognizer.await()
            val JapaneseText = JapaneseRecognizer.await()
            val DevanagariText = DevanagariRecongizer.await()
            val ChineseText = ChineseRecognizer.await()
            val LatinText = LatinRecognizer.await()

            LogDelegate.d(TAG, "recognizer done")
        }
    }

    private suspend fun performTextRecognition(image: InputImage): Text {
        val recognizer = TextRecognition.getClient(TextRecognizerOptions.Builder().setExecutor(executor).build())
        return suspendCoroutine { continuation ->
            LogDelegate.d(TAG, "call performTextRecognition 11111")
            recognizer.process(image)
                .addOnSuccessListener { text ->
                    LogDelegate.d(TAG, "latin recognize success 11111")
                    continuation.resume(text)
                }
                .addOnFailureListener { exception ->
                    continuation.resumeWithException(exception)
                }
        }
    }

    private suspend fun performChineseTextRecognition(image: InputImage): Text {
        val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().setExecutor(executor).build())
        return suspendCoroutine { continuation ->
            LogDelegate.d(TAG, "call performChineseTextRecognition 22222")
            recognizer.process(image)
                .addOnSuccessListener { text ->
                    LogDelegate.d(TAG, "chinese recognize success 22222")
                    continuation.resume(text)
                }
                .addOnFailureListener { exception ->
                    continuation.resumeWithException(exception)
                }
        }
    }

    private suspend fun performDevanagariTextRecognition(image: InputImage): Text {
        val recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().setExecutor(executor).build())
        return suspendCoroutine { continuation ->
            LogDelegate.d(TAG, "call performDevanagariTextRecognition 33333")
            recognizer.process(image)
                .addOnSuccessListener { text ->
                    LogDelegate.d(
                        TAG,
                        "Devanagari recognize success 33333"
                    )
                    continuation.resume(text)
                }
                .addOnFailureListener { exception ->
                    continuation.resumeWithException(exception)
                }
        }
    }

    private suspend fun performJapaneseTextRecognition(image: InputImage): Text {
        val recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().setExecutor(executor).build())
        return suspendCoroutine { continuation ->
            LogDelegate.d(TAG, "call performJapaneseTextRecognition 44444")
            recognizer.process(image)
                .addOnSuccessListener { text ->
                    LogDelegate.d(TAG, "Japanese recognize success 44444")
                    continuation.resume(text)
                }
                .addOnFailureListener { exception ->
                    continuation.resumeWithException(exception)
                }
        }
    }

    private suspend fun performKoreanTextRecognition(image: InputImage): Text {
        val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().setExecutor(executor).build())
        return suspendCoroutine { continuation ->
            LogDelegate.d(TAG, "call performKoreanTextRecognition 55555")
            recognizer.process(image)
                .addOnSuccessListener { text ->
                    LogDelegate.d(TAG, "Korean recognize success 55555")
                    continuation.resume(text)
                }
                .addOnFailureListener { exception ->
                    continuation.resumeWithException(exception)
                }
        }
    }
}

运行日志

2023-11-10 10:21:02.661 29695-32549 LocalOCRTestActivity    call recognizer

2023-11-10 10:21:02.663 29695-30652 LocalOCRTestActivity    call performChineseTextRecognition 22222

2023-11-10 10:21:02.665 29695-32550 LocalOCRTestActivity    call performDevanagariTextRecognition 33333

2023-11-10 10:21:02.667 29695-32549 LocalOCRTestActivity    call performKoreanTextRecognition 55555 

2023-11-10 10:21:02.668 29695-30612 LocalOCRTestActivity    call performTextRecognition 11111 

2023-11-10 10:21:02.670 29695-30650 LocalOCRTestActivity    call performJapaneseTextRecognition 44444 

2023-11-10 10:21:03.141 29695-29695 LocalOCRTestActivity    chinese recognize success 22222

2023-11-10 10:21:03.483 29695-29695 LocalOCRTestActivity    Devanagari recognize success 33333 

2023-11-10 10:21:04.015 29695-29695 LocalOCRTestActivity    Korean recognize success 55555

2023-11-10 10:21:04.269 29695-29695 LocalOCRTestActivity    latin recognize success 11111 

2023-11-10 10:21:04.678 29695-29695 LocalOCRTestActivity    Japanese recognize success 44444

2023-11-10 10:21:04.681 29695-32549 LocalOCRTestActivity    recognizer done 

需确认代码是否存在问题,以及该文本识别器是否支持并行调用。


分析与结论

代码层面的潜在问题

  1. 自定义线程池与ML Kit内部调度冲突:
    你给每个识别器指定了自定义ThreadPoolExecutor,但ML Kit的文本识别客户端本身有默认的线程调度机制,强行传入外部线程池可能干扰底层任务调度,导致任务串行执行。

  2. 协程上下文的继承限制:
    启动async时默认继承当前协程的Dispatchers.IO上下文,虽然协程层面启动了5个任务,但如果ML Kit底层对识别任务做了串行化处理,协程的并行设置无法突破这个限制。

ML Kit文本识别的并行支持情况

ML Kit文本识别本身支持并行调用,但存在以下注意点:

  • 你的线程池核心线程数设为5,理论上可以同时承载5个任务,但日志中任务完成时间依次递增,说明ML Kit底层可能对同类型识别任务做了串行化处理,或者自定义线程池配置未生效。
  • 日志中成功回调都在主线程执行,这只是回调线程的选择,不代表识别任务本身是串行的,但任务完成时间的连续递增证明实际识别过程是串行的。

优化建议

  • 移除自定义线程池:删除setExecutor(executor)调用,让ML Kit使用默认线程调度机制,验证是否能实现并行。
  • 显式指定协程调度器:在async中指定Dispatchers.Default,确保协程能在独立线程启动任务:
    val LatinRecognizer = async(Dispatchers.Default) { performTextRecognition(image) }
    
  • 复用识别器实例:每个performXXX方法都创建新的识别器实例,会带来初始化开销,建议提前初始化5个识别器并复用。

内容的提问来源于stack exchange,提问作者user3659153

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:17:31