多线程嵌入式软件中的原子操作

Posted

技术标签:

【中文标题】多线程嵌入式软件中的原子操作【英文标题】:Atomic operation in multithreaded embedded software 【发布时间】:2018-10-18 13:00:51 【问题描述】:

我一直在用 C 语言开发基于 RTOS 的嵌入式软件,我遇到了一个关于从多个线程访问共享资源的问题。我有两个问题。第一个是在状态机中设置和获取状态变量的值。下面是 StateMachine "object" 的头文件:

typedef enum
  STATE_01,
  STATE_02,
  STATE_03,
  STATE_04
state_e;

// state machine instance
typedef struct
  state_e currState;
StateMachine;

extern state_e GetState(StateMachine*);
extern void SetState(StateMachine*, state_e);

访问方法的实现如下:

state_e GetState(StateMachine *sm)
  return sm->currState;


void SetState(StateMachine *sm, state_e state)
  sm->currState = state;

我的问题是我不确定是否应该使用互斥锁来控制对状态变量的访问。我的意思是在 32 位 MCU 上读取和写入 32 位变量是原子操作。

第二个问题涉及读取包含无符号 32 位整数的数组的一项值,其中每个位存储一位变量的值。 在这里,我再次不确定是否有必要使用互斥锁。出于与上述相同的原因,我认为不会,但我想听听一些更有经验的程序员的意见。位数组“对象”的相关头文件:

typedef struct
  uint32_t BitsArray[NO_WORDS];
BitsArray;

extern uint32_t GetWordValue(BitsArray*, uint8_t);

访问方法实现:

uint32_t GetWordValue(BitsArray *ba, uint8_t word)
 return *(ba->BitsArray + word);

感谢您的任何想法。

【问题讨论】:

您正在多个并发执行上下文之间共享状态机的当前状态?那么你有一个比变量访问的原子性更大的问题。帮自己一个忙,然后回去仔细寻找替代设计。 ^^^ @andymango 所说的。如果我需要操作 SM,我通常将事件(生产者-消费者队列)排队到一个线程中,该线程可以单独访问状态数据。其他任何事情都会很快变成无法调试的混乱。 此外,您的问题的答案部分取决于目标系统是否具有多个 CPU 内核。如果它有多个,那么同时在不同内核上运行的不同线程可能对内存中的内容有不同的看法。 (谷歌的“缓存一致性”。)在这种情况下,您的 RTOS 有责任为您提供在不同线程之间安全共享数据所需的工具。 【参考方案1】:

你的两个问题确实是同一个问题。

32 位 MCU 没有任何意义,除非您反汇编代码并验证该操作确实是一个单指令。 C 代码通常不是这种情况。

您通常有 2 条或更多指令,例如:“将值从堆栈加载到寄存器”、“使用寄存器执行操作”,在这种情况下,您的 MCU 获得多少位并不重要。您可以在两条指令之间获得中断或上下文切换。

即使您可以验证机器代码是原子的,也不一定是稳定的事态。对代码进行更改,添加更多变量,重新链接,突然之前的原子代码不再是原子的,反之亦然。

C 根本无法保证原子性。如果您不信任反汇编,可以使用一些替代方法:

C11_Atomic。 编写内联汇编程序。 使用互斥锁或类似的同步机制。

【讨论】:

以上是关于多线程嵌入式软件中的原子操作的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式软件开发 笔试和面试笔记 (未完结)

C/C++ 中的多线程状态机实现

(14)嵌入式软件开发工程师技能要求总结

嵌入式软件开发杂谈:Linux下最大能创建多少线程?

嵌入式软件开发杂谈:Linux下最大能创建多少线程?

嵌入式软件是这样 debug 的