C++ - Clutter 1.0 - 在线程中调用函数导致段错误

6

我正在尝试从额外的线程中调用clutter函数,但遇到了困难。使用了boost::thread进行线程处理和clutter库1.0。

具体来说,该线程包含一个循环函数,定期发出带有x和y坐标参数的boost::signals2::signal。该信号连接到一个函数,将这些变量传递给clutter,即在以下代码中的x,y:

clutter_stage_get_actor_at_pos(CLUTTER_STAGE(actor), CLUTTER_PICK_ALL, x, y);

但是我遇到了段错误(segfault)。

显然,clutter有一些线程处理例程。我尝试在启动clutter_main()之前调用:

g_thread_init(NULL);

clutter_threads_init();

我还尝试使用以下代码包装clutter函数:

clutter_threads_enter();

clutter_stage_get_actor_at_pos(CLUTTER_STAGE(actor), CLUTTER_PICK_ALL, x, y);

clutter_threads_leave();

但这也没有解决问题。

非常感谢任何提示!

补充说明

我已经创建了一个最小化的样例来展示我的问题。我已经按照建议对clutter_main()例程进行了“保护”。似乎可以从独立线程中使用clutter的某些函数(例如设置stage颜色或actor位置)。但我的代码仍然存在问题吗?

#include <clutter/clutter.h>
#include <boost/thread.hpp>


ClutterActor *stage;
ClutterActor* rect = NULL;


void receive_loop()
{
while(1)
{
    sleep(1);
    clutter_threads_enter();

    ClutterActor* clicked =  clutter_stage_get_actor_at_pos(CLUTTER_STAGE(stage), CLUTTER_PICK_ALL,300, 500);
    
    clutter_threads_leave();
}

}


int main(int argc, char *argv[]) 
{

    clutter_init(&argc, &argv);

g_thread_init(NULL);
clutter_threads_init();


    stage = clutter_stage_get_default();
    clutter_actor_set_size(stage, 800, 600);


rect = clutter_rectangle_new();
clutter_actor_set_size(rect, 256, 128);
clutter_actor_set_position(rect, 300, 500);
clutter_group_add (CLUTTER_GROUP (stage), rect);    


    clutter_actor_show(stage);


boost::thread thread = boost::thread(&receive_loop);


clutter_threads_enter();
    clutter_main();
clutter_threads_leave();

    return 0;
}

改用pthread而不是boost::thread,因为你没有使用任何Boost魔法,这里的大多数人都没有安装它。 - karlphillip
4个回答

6

好的,我认为我找到了答案。

Clutter文档概述

在“线程模型”部分中,它说:

在多线程环境中使用Clutter API的唯一安全和可移植的方法是从调用clutter_init()和clutter_main()的线程中访问API。

使用Clutter与线程的常见模式是使用工作线程执行阻塞操作,然后在线程完成时安装空闲或超时源与结果。

Clutter提供了g_idle_add()和g_timeout_add()的线程感知变体,这些变体在调用提供的回调之前获取Clutter锁:clutter_threads_add_idle()和clutter_threads_add_timeout()。

因此,我的对最小样本代码的更正是将receive_loop()改为

void receive_loop()
{
while(1)
{
    sleep(1);

    int pos[2];
    pos[0] = 400;
    pos[1] = 200;
    
    clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
                             get_actor,
                             &pos,
                             NULL);
}
}

并添加get_actor函数(如所提及文档页面上的示例代码)

static gboolean
get_actor (gpointer data)
{
    int* pos = (int*) data;
    ClutterActor* clicked = clutter_stage_get_actor_at_pos(CLUTTER_STAGE(stage), CLUTTER_PICK_ALL, pos[0], pos[1]);

    return FALSE;
}

clutter_threads_add_idle_full 会处理线程锁等问题。


是的,那就是正确的答案。(在使用Clutter多线程程序中已经使用了几年了。) - Bob Murphy

0

我尝试运行了你的代码,看起来你做得很好,虽然我不是Clutter方面的专家。我还在gdb下运行了你的程序,发现了一些有趣的事情:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb799db70 (LWP 3023)]
0x002d97c6 in glDisable () from /usr/lib/nvidia-current/libGL.so.1
(gdb) thread apply all bt

Thread 2 (Thread 0xb799db70 (LWP 3023)):
#0  0x002d97c6 in glDisable () from /usr/lib/nvidia-current/libGL.so.1 
#1  0x001b3ec3 in cogl_disable_fog () from /usr/lib/libclutter-glx-1.0.so.0
#2  0x0018b00a in ?? () from /usr/lib/libclutter-glx-1.0.so.0
#3  0x0019dc82 in clutter_stage_get_actor_at_pos () from /usr/lib/libclutter-glx-1.0.so.0
#4  0x080498de in receive_loop () at seg.cpp:19

显然,崩溃发生在/usr/lib/nvidia-current/libGL.so.1中的glDisable()。请注意,我在我的GeForce 8600 GT上使用了NVIDIA的OpenGL驱动程序。

您能否确认您的应用程序在其他视频卡(非NVIDIA)的计算机上也会崩溃?我怀疑崩溃不是由于NVIDIA的OpenGL实现中的错误引起的。

对我来说,似乎*clutter_threads_enter/leave()*没有保护*clutter_stage_get_actor_at_pos()*,因为我测试了作为回调调用的*receive_loop()*:

g_signal_connect(stage, "button-press-event", G_CALLBACK(receive_loop), NULL);

我们知道你的代码看起来没问题。

我鼓励您将问题发送到Clutter讨论和帮助邮件列表:clutter-app-devel-list

这是一个邮件列表,为使用Clutter、其集成库或基于Clutter的工具包的应用程序开发人员提供支持。


不幸的是,那并没有解决我的问题。请看我对问题的补充说明。感谢您的建议! - verb-sap

0

您可以使用clutter_threads_add_idle来更新ClutterActor,或者您需要修复clutter_threads_enter/leave以切换OpenGL上下文,以便您可以在线程内使用它。

崩溃

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb799db70 (LWP 3023)]
0x002d97c6 in glDisable () from /usr/lib/nvidia-current/libGL.so.1
(gdb) thread apply all bt

Thread 2 (Thread 0xb799db70 (LWP 3023)):
#0  0x002d97c6 in glDisable () from /usr/lib/nvidia-current/libGL.so.1 
#1  0x001b3ec3 in cogl_disable_fog () from /usr/lib/libclutter-glx-1.0.so.0
#2  0x0018b00a in ?? () from /usr/lib/libclutter-glx-1.0.so.0
#3  0x0019dc82 in clutter_stage_get_actor_at_pos () from /usr/lib/libclutter-glx-1.0.so.0
#4  0x080498de in receive_loop () at seg.cpp:19

因为调用线程没有获取到OpenGL上下文,所以程序崩溃了。


0

我在 Python 绑定的 clutter 中遇到了非常类似的情况。我从未能够使 Clutter 线程支持按照我想要的方式工作。

最终解决问题的方法是使用空闲进程(在 Python 中使用 gobject.idle_add)将我需要完成的工作推入主要的 clutter 线程中。这样,我只有一个线程进行 clutter 调用,一切都很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接