2

在嵌入式编程中使用多个线程时,我感到有点不知所措,因为每个共享资源最终都带有一个受互斥锁保护的 getter/setter。

我真的很想了解以下类型的吸气剂

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);

只锁定变量的副本而不是完全执行?

EDIT2:鉴于“a”,“b”和“c”可能存在getter和setter。这样做在性能/或其他方面是否有问题?

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());
}

我问这个是因为我无缘无故地顺序锁定和重新锁定同一个互斥锁。

4

2 回答 2

1

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”可能存在 getter 和 setter。这样做在性能/或其他方面是否有问题?

该代码是正确的。如果可以的话,就性能而言,每个变量都有一个互斥锁肯定会好得多。只有一个互斥锁,任何持有互斥锁的线程都会“阻塞”任何其他试图锁定它的线程,即使它们试图访问不同的变量。

如果您不能使用多个互斥锁,则需要在这两个选项之间进行选择:

  1. 锁定在吸气剂内部:

    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);
    
  2. 锁定在 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);
    

    在这一点上,你甚至不需要吸气剂,你可以这样做:

    os_mutex_get(mutex, OS_WAIT_FOREVER);
    int var1 = a;
    int var2 = b;
    os_mutex_put(mutex);
    

如果您的代码经常请求多个值,那么在 getter之外锁定/解锁会更​​好,因为它会减少开销。作为替代方案,您也可以将锁定保留在内部,但创建不同的函数来检索多个变量,以便互斥锁只被锁定和释放一次。

另一方面,如果您的代码很少请求多个值,则可以将锁定保留每个 getter 中。

无法提前说出最佳解决方案是什么,您应该运行不同的测试并查看最适合您的方案的解决方案。

于 2020-02-04T16:33:51.410 回答
1

如果浮点赋值可以被认为是原子的

_Atomic除非您使用 C11或内联汇编程序,否则 C 中的任何内容都不能被认为是原子的。底层硬件无关紧要,因为即使可以在给定硬件上的单个指令中读取某个大小的字,也永远不能保证某个 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);,那么是的,这很有意义。这就是您理想地使用互斥锁的方式:最小化互斥锁之间的代码,以便它只是原始变量复制而不是其他任何东西。

于 2020-02-05T11:42:07.913 回答