【发布时间】:2016-08-22 13:49:02
【问题描述】:
我偶然发现了一个我无法理解的非常奇怪的问题。先说一点背景故事:
我正在尝试运行 JavaScriptCore 并将其用作 Android 应用程序的某种脚本语言。问题是,主线程上的堆栈大小在较旧的 Android 版本上非常有限(类似于 API 16 上的 12k)。但是,我仍然想在主线程上调用 JS,让它回调以请求事物并让所有这些看起来是同步的。没问题 - 我会抽出几个 channe...khm... SynchronousQueues 并来回反弹执行。 Here's what my code looks like.
这很简单 - 每次调用 defer - 它都会反弹到另一个线程并从那里继续。唯一的问题是,它不起作用。在执行 Javascript 代码的实际用例中,它在某些时候会非常可靠地失败,尽管对于模拟器和不同的设备来说不是在同一个地方。 Logcat 总是看起来很无害:
I/JavaScriptCore: Lockstep [Main]: Defer
I/JavaScriptCore: Lockstep [Main]: Send EXECUTE_FUNC
I/JavaScriptCore: Lockstep [Background]: Receive EXECUTE_FUNC
I/JavaScriptCore: Lockstep [Background]: Defer
I/JavaScriptCore: Lockstep [Background]: Send EXECUTE_FUNC
然而,第二个 EXECUTE 永远不会被 main 接收到,即使 put 通过了。据我了解,同步队列甚至都不可能做到这一点。查看线程转储,后台线程正在运行循环中等待下一条消息,而主线程则停在 incoming.take 上。没有其他线程与之交互。
在我的一个设备上,我可以在它停止工作的确切时刻设置一个条件断点,并且我可以在 MAIN 等待那个 EXECUTE 消息时暂停它。该消息是非空的,此时的foregroundQueue 正在工作,我可以从Android Studio 轮询它,无论是否超时,取它的大小等等。一旦我跨过所有操作,就会挂起。
当然,我怀疑 JNI 的恶作剧,但在 Logcat 中根本没有内存转储、分段错误或任何警告。
此外,这不仅仅是采取 - 即使我在非常肮脏的忙碌等待中这样做:
Message msg = incoming.poll();
if(msg == null) {
Thread.sleep(20);
continue;
}
Main 卡在 poll 上,后台线程每 20 毫秒愉快地在另一个队列上运行一次。
我尝试用一个非常懒惰的阶乘嵌套延迟,它喜欢睡很多,它没有问题去 200 深,尽管整数溢出:
LockstepThread t = new LockstepThread();
int deferredFactoriel(final int n) {
if(n == 0) {
return 1;
}
return n * t.defer(new Functor<Integer>() {
@Override
public Integer call() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return deferredFactoriel(n-1);
}
});
}
@Override
public void onCreate() {
super.onCreate();
for(int i=0; i<200; ++i) {
Log.i("Test", i+"! = " + deferredFactoriel(i));
}
...
最奇怪的是,我使用什么同步并不重要。 SynchronizedQueue、ArrayBlockingQueue、LinkedBlocking 队列 - 它总是在同一个位置失败,并使用完全相同的线程转储。见鬼,我什至创建了自己的exchanger,只是为了确保我不会发疯,但它仍然以同样的方式卡住。
所以是的,我完全被难住了。有什么想法吗?任何有关调试的帮助将不胜感激。
【问题讨论】:
-
请展示一个示例 Functor,我怀疑您的 Functor 对 defer/runLoop 的嵌套调用错误,导致死锁。
-
正如@glee8e 所说:这看起来像是
Functor中的代码有问题。您的示例代码没有显示如何从后台线程调用defer,但您的日志跟踪显示了这种情况。
标签: java android concurrency android-ndk