在嵌入式编程中使用多个线程时,我感到有点不知所措,因为每个共享资源最终都有一个受互斥锁保护的 getter/setter。
我真的很想了解以下类型的 getter 是否
static float static_raw;
float get_raw() {
os_mutex_get(mutex, OS_WAIT_FOREVER);
float local_raw = static_raw;
os_mutex_put(mutex);
return local_raw ;
}
有意义,或者如果float
赋值可以被认为是原子的,例如对于 ARM(不同于例如 64 位变量(,则使其变得多余。
我可以理解这样的事情:
raw = raw > VALUE ? raw + compensation() : raw;
多次处理值,但在读取或返回它时呢?
你能把我的想法说清楚吗?
编辑 1: 关于下面的第二个问题。 假设我们在时间执行方面有一个"重"函数,我们称之为
void foo(int a, int b, int c)
其中 a、b、c 可能是来自共享资源的值。 当调用 foo 函数时,它是否应该被互斥锁包裹,即使它只需要一个值的副本,也要锁定它很长时间?例如
os_mutex_get(mutex, OS_WAIT_FOREVER);
foo(a,b,c);
os_mutex_put(mutex);
这样做有意义吗
os_mutex_get(mutex, OS_WAIT_FOREVER);
int la = a;
int lb = b;
int lc = c;
os_mutex_put(mutex);
foo(la,lb,lc);
仅锁定变量的副本而不是完全执行?
编辑2: 鉴于可能存在"a"、"b"和"c"的获取者和设置器。 在性能方面/或其他任何事情方面做这样的事情有问题吗?
int static_a;
int get_a(int* la){
os_mutex_get(mutex, OS_WAIT_FOREVER);
*la = static_a;
os_mutex_put(mutex);
}
或
int static_b;
int get_b(){
os_mutex_get(mutex, OS_WAIT_FOREVER);
int lb = static_b;
os_mutex_put(mutex);
return lb;
}
将它们用作
void main(){
int la = 0;
get_a(&la);
foo(la,get_b());
}
我问这个是因为 im 无缘无故地按顺序锁定和重新锁定同一个互斥锁。
如果浮点赋值可以被认为是原子的
在 C 语言中,除非使用 C11_Atomic
或内联汇编程序,否则任何内容都不能被视为原子。底层硬件是无关紧要的,因为即使可以在给定硬件上的单个指令中读取特定大小的单词,也永远无法保证某个 C 指令只会产生单个指令。
这样做有意义吗
os_mutex_get(mutex, OS_WAIT_FOREVER); int la = a; int lb = b; int lc = c; os_mutex_put(mutex); foo(a,b,c);
假设你的意思是foo(la,lb,lc);
,那么是的,这很有意义。这就是理想情况下应该如何使用互斥锁:最小化互斥锁之间的代码,使其只是原始变量复制,而不是其他内容。
C 标准没有规定任何关于赋值运算符的原子性。您不能考虑赋值原子,因为它完全依赖于实现。
但是,在 C11 中,可以使用_Atomic
类型限定符(C11 §6.7.3,此处第 121 页(来声明要以原子方式读取和写入的变量,因此您可以执行以下操作:
static _Atomic float static_raw;
float get_raw(void) {
return static_raw;
}
如果这样做,请不要忘记使用-std=c11
进行编译。
解决您的第一次编辑:
当调用 foo 函数时,它是否应该被互斥锁包裹,即使它只需要一个值的副本,也要锁定它很长时间?
虽然这是正确的,但它肯定不是最好的解决方案。如果函数只需要变量的副本,那么您的第二个代码段无疑要好得多,并且应该是理想的解决方案:
os_mutex_get(mutex, OS_WAIT_FOREVER);
int la = a;
int lb = b;
int lc = c;
os_mutex_put(mutex);
foo(la,lb,lc);
如果你锁定了整个函数,你将阻止任何其他线程试图获取锁的时间比需要的时间长得多,从而减慢一切。在调用函数之前锁定并传递值的副本将只锁定所需的时间,而将更多的空闲时间留给其他线程。
解决您的第二次编辑问题:
鉴于可能存在"a"、"b"和"c"的获取者和设置器。在性能方面/或其他任何事情方面做这样的事情有问题吗?
该代码是正确的。就性能而言,如果可以的话,每个变量有一个互斥锁肯定会好得多。只有一个互斥锁时,任何持有互斥锁的线程都将"阻止"任何其他试图锁定它的线程,即使它们试图访问不同的变量。
如果您不能使用多个互斥锁,则需要在这两个选项之间进行选择:
锁定吸气器内部:
void get_a(int* la){ os_mutex_get(mutex, OS_WAIT_FOREVER); *la = static_a; os_mutex_put(mutex); } void get_b(int* lb){ os_mutex_get(mutex, OS_WAIT_FOREVER); *lb = static_b; os_mutex_put(mutex); } /* ... */ int var1, var2; get_a(&var1); get_b(&var2);
锁定在 getter 之外(将职责留给调用者(:
int get_a(void){ return static_a; } int get_b(void){ return static_b; } /* ... */ os_mutex_get(mutex, OS_WAIT_FOREVER); int var1 = get_a(); int var2 = get_b(); os_mutex_put(mutex);
在这一点上,你甚至不需要有getter,你可以这样做:
os_mutex_get(mutex, OS_WAIT_FOREVER); int var1 = a; int var2 = b; os_mutex_put(mutex);
如果您的代码经常请求多个值,那么在 getter之外锁定/解锁会更好,因为它会导致更少的开销。作为替代方案,您也可以将锁定保留在内部,但创建不同的函数来检索多个变量,以便互斥锁仅锁定和释放一次。
另一方面,如果您的代码很少请求多个值,那么在每个 getter中保持锁定是可以的。
无法提前说明最佳解决方案是什么,您应该运行不同的测试,看看什么最适合你的方案。