我有以下问题:
我通过UART(每10ms)接收传感器数据,我想在gtk中处理这些数据。这个想法是在特定的时间间隔内(例如每100ms)处理这些数据。因此,我使用g_timeout_add()函数。 被调用的函数包含 UART 通信的公共集 ->在我的情况下它会阻塞,直到我收到 10 个字符。 这就是问题所在 - 在我的 read()(-> 系统调用)函数中,程序挂断了。
我的串行设置如下:
struct termios oldtio,newtio;
tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
// set input mode (non-canonical, no echo,...)
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; // inter-character timer unused
newtio.c_cc[VMIN] = 10; // blocking read until 10 chars received
res = read(fd,buf,20); // returns after 10 chars have been input
无论我如何更改g_timeout_add函数中的间隔参数,都不会发生任何事情。
我读了很多关于这个问题的文章,但我找不到任何关于我的问题的解释。此外,我什至找不到处理UART-GTK组合的线程。
代码很长,因此我现在只想问你,你是否认为这个想法基本上可以工作,或者我的概念中是否存在根本问题?
仅供参考:我在没有 GUI (->GTK) 的情况下测试了这段代码,它运行良好。在纯C中,我可以读取数据并打印出来。
我很高兴任何答案。
GTK+ 像大多数工具包一样是事件驱动的。事件在主循环中调度和处理,主循环是进程的主线程。因此,您不能执行阻塞调用(例如sleep(5)
),也不能执行在回调中执行需要很长时间的处理。
想想两个工作线程(主循环和回调),它们只有洞要挖,只有一个铲子(CPU 时间)。主循环看到有一个超时事件需要处理,并将铲子交给回调,以便它可以工作。如果回调需要铲子 5 小时,则主循环无法完成其工作(例如绘制 UI 窗口),也无法将铲子交给任何其他等待它的回调。
在您的情况下,您正在以阻塞方式等待数据。您正在等待 10 个字节到达。如果这需要他们 5 个小时才能到达,你保留铲子。要解决此问题,您需要:
- 在单独的线程中执行阻塞调用(因此两个工作人员都有铲子并且可以并行工作)
- 或者异步进行(也许使用 GTask?自己从来没有用过)
- 或者(效率较低)做一些轮询,这已经是你已经在做的事情,因为你每 100 毫秒寻找一次数据
对于轮询解决方案,您可能可以实现VMIN = 0 and VTIME > 0
情况,但您需要处理必须重建消息的事实,因为您可能会一次接收到比预期更多的字节。
对于线程解决方案,我会使用GThread
像您现在所做的那样执行阻塞read
调用,并删除g_timeout_add
。您不需要它,因为您将从轮询模型更改("我收到数据了吗?每秒 10 次)到通知模型("您已收到数据!相反,当您在线程中读取了一些字节时,请使用g_idle_add
将它们发送到主循环。在关联的回调中,你将收到数据,并能够将其呈现给用户。但请记住,切勿从主线程以外的线程调用 GTK+ 函数,因为该工具包不是线程安全的。这就是我们g_idle_add
的原因(来自 GLib,没关系)。