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
需确认代码是否存在问题,以及该文本识别器是否支持并行调用。
分析与结论
代码层面的潜在问题
自定义线程池与ML Kit内部调度冲突:
你给每个识别器指定了自定义ThreadPoolExecutor,但ML Kit的文本识别客户端本身有默认的线程调度机制,强行传入外部线程池可能干扰底层任务调度,导致任务串行执行。协程上下文的继承限制:
启动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
相关产品推荐
相关产品推荐

