C 组织多线程数据记录

C 组织多线程数据记录,c,linux,multithreading,code-organization,C,Linux,Multithreading,Code Organization,我正在为运行Linux发行版(使用Yocto构建)的嵌入式目标开发一个C项目。我是Linux嵌入式世界的新手,我必须构建一个数据记录系统 我已经在学习如何使用线程,我正在考虑项目组织 以下是我的想象: 多线程从不同接口、CAN总线、I2C。。。(不同采样率) 一个线程,采样率为200ms,用于填充csv文件 一个线程,采样率为3秒,用于通过http请求发送数据 线程将在CAN信息或外部事件时停止 我不知道组织这个项目的最佳方式是什么。我看到两种方法,第一种是启动程序创建每个线程,并在whil

我正在为运行Linux发行版(使用Yocto构建)的嵌入式目标开发一个C项目。我是Linux嵌入式世界的新手,我必须构建一个数据记录系统

我已经在学习如何使用线程,我正在考虑项目组织

以下是我的想象:

  • 多线程从不同接口、CAN总线、I2C。。。(不同采样率)
  • 一个线程,采样率为200ms,用于填充csv文件
  • 一个线程,采样率为3秒,用于通过http请求发送数据
  • 线程将在CAN信息或外部事件时停止
我不知道组织这个项目的最佳方式是什么。我看到两种方法,第一种是启动程序创建每个线程,并在while循环中等待事件监视以停止它们。第二种方法是启动程序将其他二进制文件作为线程执行。 在这两种情况下,我不知道如何在线程之间共享数据

你能告诉我你的经历吗

多谢各位

编辑: 首先,非常感谢@Glärbo的解释。学习多线程机制真的很有帮助

我已经成功地测试了它

为了将来的读者,我画了一些图表来说明@Glärbo的答案


我会用一种简单的多生产者、单消费者的方法来简化

假设每个数据项都可以用一个数值来描述:

struct值{
结构值*下一步;/*形成数据项的单链接列表*/
struct sensor*from;/*标识这是哪个传感器值*/
struct timespec when;/*传感器读取时间(UTC)*/
双值;/*数值*/
};
我将使用两个值列表:一个用于已接收但未存储的传感器读数,另一个用于未使用的值存储桶。通过这种方式,您不需要动态分配或释放值存储桶,除非您愿意(通过操纵未使用的列表)

两个列表都由互斥锁保护。由于未使用列表可能是空的,因此我们需要一个条件变量(每当向其添加新的未使用值时,该变量都会发出信号),以便线程可以等待一个条件变量变为可用。接收到的列表同样需要一个条件变量,因此,如果消费者(数据存储者)需要它们时它恰好为空,它可以等待至少一个条件变量出现

静态pthread\u mutex\u t unused\u lock=pthread\u mutex\u初始值设定项;
静态pthread_cond_t unused_wait=pthread_cond_初始值设定项;
静态结构值*unused_list=NULL;
静态pthread_mutex_t received_lock=pthread_mutex_初始值设定项;
静态pthread_cond_t received_wait=pthread_cond_初始值设定项;
静态结构值*received_list=NULL;
对于未使用的列表,我们需要三个帮助程序:一个用于从头开始创建新的未使用的值项(您最初调用它来为每个传感器创建两个或三个值项,再加上几个),然后,如果您认为需要它们(例如,如果您在运行时添加新的传感器):

int unused\u创建(void)
{
结构值*v;
v=malloc(sizeof*v);
如果(!v)
返回烯醇化酶;
v->from=NULL;
pthread_mutex_lock(&unused_lock);
v->next=未使用的\u列表;
未使用列表=v;
pthread_cond_信号(&unused_wait);
pthread_mutex_unlock(&unused_lock);
返回0;
}
另外两个用于从列表中获取并将值项放回列表:

struct value*unused\u get(void)
{
结构值*v;
pthread_mutex_lock(&unused_lock);
而(!未使用的列表)
pthread_cond_wait(&unused_wait,&unused_lock);
v=未使用的_列表;
未使用列表=未使用列表->下一步;
pthread_mutex_unlock(&unused_lock);
v->from=NULL;
返回v;
}
void unused_put(结构值*v)
{
v->from=NULL;
pthread_mutex_lock(&unused_lock);
v->next=未使用的\u列表;
未使用列表=v;
pthread_cond_信号(&unused_wait);
pthread_mutex_unlock(&unused_lock);
}
上面的想法是,当
from
成员为空时,该项未使用(因为它不是来自任何传感器)。从技术上讲,我们不需要在每个阶段都将其清除为NULL,但我喜欢彻底:这不像设置它是一个昂贵的操作

传感器读取传感器读数,使用例如
clock\u gettime(clock\u REALTIME,×pec)
获取当前时间,然后使用
unused\u get()
获取新的未使用项。(顺序很重要,因为如果没有空闲项,
unused\u get()
可能需要一些时间。)然后,他们填写字段,并调用以下
received\u put()
将读取结果预先添加到列表中:

void已接收\u put(结构值*v)
{
pthread_mutex_lock(&received_lock);
v->next=已接收\u列表;
接收到的_列表=v;
pthread_mutex_信号(&received_wait);
pthread_mutex_unlock(&received_lock);
}
只有一个线程定期收集并存储所有接收到的传感器读数。它可以保存一组最新的读数,并定期发送这些读数。我们不应该重复调用一些
received_get()
,直到没有更多未处理的接收值,而应该使用一个函数返回它们的整个列表:

struct value *received_getall(void)
{
    struct value *v;
    pthread_mutex_lock(&received_lock);
    while (!received_list)
        pthread_cond_wait(&received_wait, &received_lock);
    v = received_list;
    received_list = NULL;
    pthread_mutex_unlock(&received_lock);
    return v;
}
消费者线程存储/发送总结和阅读,应获取整个列表,然后逐一处理。处理完每个项目后,应将其添加到未使用列表中。换句话说,类似于

struct value*all,v;
而(1){
all=接收_getall();
而(全部){
v=全部;
全部=全部->下一步;
v->next=NULL;
/*存储/汇总价值项目v*/
未使用的_put(v);
}
}
如您所见,当消费者线程处理传感器值项目时,传感器线程可以为下一轮添加新读数,只要有enoug