GtK+3.0 多线程应用

Posted

技术标签:

【中文标题】GtK+3.0 多线程应用【英文标题】:GtK+3.0 multithreaded application 【发布时间】:2017-12-25 10:06:58 【问题描述】:

这是我的问题: 我正在开发一个由以下组件组成的多线程应用程序:

GUI 线程-> GTK 帮助线程 --> 检查与 JACK 服务器的连接 jack 的 RT 线程--> 做一些声音处理的东西

我已经实现了一个按钮小部件,它只接收来自助手和 RT 线程的信号,在其回调函数中修改 gui。

所以,我的问题是:谁在修改 GUI?我使用 gtk_main() 的助手/RT 线程或 gui 线程?

感谢您的合作!

编辑:我添加了代码 /** @file JPLowPassFilter.c * * @brief 这是一个实现数字低频通滤波器的简单客户端 */

#include "JPLowPassFilter.h" 
jack_port_t *input_port;
jack_port_t *output_port;

jack_default_audio_sample_t tmp;
int first=1;
appData* mainData;
jack_default_audio_sample_t tmp;

/*Code for port_registration_callback */
void registrationPort(jack_port_id_t port, int reg, void *arg)

    return;


/*Code for client_registration_callback */
void registrationClient(const char* name, int reg, void *arg)

    return;


/**
 * The process callback for this JACK application is called in a
 * special realtime thread once for each audio cycle.
 * Must not block!
 */
int process (jack_nframes_t nframes, void *arg)
 
    int i;
    float alfa=mainData->alfa;
    jack_default_audio_sample_t *in, *out;
    in = jack_port_get_buffer (input_port, nframes);
    out = jack_port_get_buffer (output_port, nframes);
    for( i=0; i<nframes; i++) 
        if(first==1)
            tmp=in[i];
            first=0;
        
        else
            tmp=tmp*alfa+(1.0f-alfa)*in[i];
            //tmp=tmp*(1.0f-alfa)+alfa*in[i];
        
        out[i]=tmp;
    
    //fprintf (stderr, ".");
    return 0;      


/**
 * JACK calls this shutdow_callback if the server ever shuts down or
 * decides to disconnect the client
 */
void jack_shutdown (void *arg)
 
    mainData->state=NOT_WORKING;
    jack_port_unregister(mainData->client,input_port);
    jack_port_unregister(mainData->client, output_port);    
    g_signal_emit_by_name (GTK_BUTTON(mainData->init),"clicked");


/* JACK calls this function whenever there is an xrun */
int xrun_function(void *arg)

    fprintf (stderr, "--XRUN OCCURRED--\n");    


void* threadCode(void* val)

    const char *client_name = CLIENT_NAME;
    const char *server_name = NULL;
    mainData=(appData*) val;
    /* if server isn't present, don't start it!*/ 
    jack_options_t options =JackNoStartServer; 
    jack_status_t status;
    do
        /* try to open a client connection to the JACK server */
        mainData->client = jack_client_open (client_name, options, &status, server_name);
        if (mainData->client == NULL) 
        
            fprintf (stderr, "jack_client_open() failed, "
                 "status = 0x%2.0x\n", status);
            if (status & JackServerFailed) 
                fprintf (stderr, "Unable to connect to JACK server\n");
            
            sleep(3);
        
        else
        
            fprintf (stderr, "Connected to JACK server\n");
            mainData->state=INIT;
            /*  CALLBACKS*/
            jack_set_process_callback (mainData->client, process, 0);
            jack_on_shutdown (mainData->client, jack_shutdown, mainData);
            jack_set_xrun_callback(mainData->client,xrun_function, 0);  
            jack_set_port_registration_callback (mainData->client, registrationPort,NULL);
            jack_set_client_registration_callback(mainData->client, registrationClient,NULL);

            /* PORTS */
            input_port = jack_port_register (mainData->client, "input", JACK_DEFAULT_AUDIO_TYPE,JackPortIsInput, 0);
            output_port = jack_port_register (mainData->client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
            mainData->state=INIT;
            if ((input_port == NULL) || (output_port == NULL)) 
            
                fprintf(stderr, "no more JACK ports available\n");
                mainData->state=NOT_WORKING;
            
            /* STARTS */
            else if (jack_activate (mainData->client)) 
                fprintf (stderr, "cannot activate client");
                jack_port_unregister(mainData->client,input_port);
                jack_port_unregister(mainData->client, output_port); 
                mainData->state=NOT_WORKING; 
            
            else  
            
                fprintf (stderr, "Client ready to Run\n");
                mainData->state=WORKING;
                mainData->portsName[0]=jack_port_name(input_port);
                mainData->portsName[1]=jack_port_name(output_port);
            
             //can be written to
                        mainData->inputList=jack_get_ports(mainData->client,NULL, NULL,JackPortIsInput);
                        //can be read from
            mainData->outputList=jack_get_ports(mainData->client,NULL, NULL,JackPortIsOutput);
            g_signal_emit_by_name (GTK_BUTTON(mainData->init),"clicked");
            while(mainData->state==WORKING)
            
                sleep(5);
                fprintf (stderr, ".");
            
            fprintf (stderr, "\n");
                jack_free(mainData->inputList);
            jack_free(mainData->outputList);
            mainData->outputList=NULL;
            mainData->inputList=NULL;
            jack_client_close (mainData->client);       
        
        fprintf (stderr, "---RECONNECT---\n");
    while(mainData->state==NOT_WORKING);
    fprintf (stderr, "Ended!\n");

    pthread_exit(NULL);

【问题讨论】:

Gtk 是线程感知的,但不是线程安全的。 Gtk 函数/方法应该从主线程调用,也就是 gtk_main/mainloop 所在的线程。然而,GLib 是线程安全的,但有一些注释。 所以我不应该从其他 2 个线程调用信号?我仍然无法理解哪个线程在做什么...... 是的,信号是从“主线程”调用的。基本上,必须从主线程调用用于更新 GUI 的函数/方法。如果您的问题没有更好的解释或代码,我们将无法为您提供更多帮助。 按照您的建议,我上传了发出信号的代码,但界面仍然无法正常工作,实际上有时它不会正确更新两个组合框或只是崩溃 “所以,我的问题是:谁在修改 GUI?助手/RT 线程还是我使用 gtk_main() 的 gui 线程?” 我不是确定这是一个问题。你的措辞让你不清楚你在问什么,如果有的话。你是说正在发生一些神秘的修改,并要求我们从心理上猜测谁负责?或者你在问你应该使用哪个线程来故意修改东西?这里似乎没有我能辨别的任何问题或问题陈述。 【参考方案1】:

根据 cmets,建议将 g_signal_emit_by_name 函数替换为 g_async_queue_* 函数。

假设mainData-&gt;init 指向在主线程中创建的GAsyncQueue,而不是实际的按钮。

然后你可以在你的线程上使用:

g_async_queue_push(G_ASYNC_QUEUE(mainData->init), data);

数据可以包含一个简单的标志来指示状态和/或状态变化

然后在您的主线程上,当您设置 UI 时,您可以添加空闲处理程序:

my_queue = g_async_queue_new();
...
g_idle_add ((GSourceFunc) check_async_queue, my_queue); 

你的check_async_queue 可能是这样的:

gboolean check_async_queue (gpointer user_data) 
   gpointer queue_data;

   queue_data = g_async_queue_try_pop (G_ASYNC_QUEUE(user_data));

   if (queue_data != NULL) 
      // We have data, do something with 'queue_data'
      // and update GUI

    else 
      // no data, probably do nothing

   

   return TRUE; // can be G_SOURCE_CONTINUE instead of TRUE

返回值将指示check_async_queue函数是否应该继续运行,因此您可以有条件删除该函数。

这将允许您拥有一个简单的单向消息队列,您可以使用该消息队列将信息从工作线程传递到主线程。

【讨论】:

以上是关于GtK+3.0 多线程应用的主要内容,如果未能解决你的问题,请参考以下文章

gtk+多线程的程序实例

Cocos2d-x 3.0多线程异步资源载入

Cocos2d-x 3.0多线程异步资源载入代码

GTK+ Toggle动作

哪些 GUI 框架最适合多线程 Python 程序?

Swift 3.0 CoreData 创建多上下文