【问题标题】:getter and setters best practice with mutex使用互斥锁的 getter 和 setter 最佳实践
【发布时间】:2020-05-20 11:13:07
【问题描述】:

在嵌入式编程中使用多个线程时,我感到有点不知所措,因为每个共享资源最终都会有一个受互斥体保护的 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);

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

编辑2: 鉴于“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());
}

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

【问题讨论】:

    标签: c embedded race-condition freertos shared-resource


    【解决方案1】:

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

    Nothing 在 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);,那么是的,这很有意义。这就是您理想地使用互斥锁的方式:最小化互斥锁之间的代码,以便它只是原始变量复制而不是其他任何东西。

    【讨论】:

    • 你也可以看看 EDIT 2 吗?
    • @Luigi 获取互斥锁总是会影响性能。您可以通过将共享相同互斥锁的所有相关变量放在一个结构中来改进它,然后提供get_aget_abc getter,其中get_abc 返回一个包含所有 3 个的结构,但只获取一次互斥锁。
    【解决方案2】:

    C 标准对赋值运算符的原子性没有任何规定。您不能考虑分配原子,因为它完全依赖于实现。

    但是,在 C11 中,_Atomic type qualifier(C11 §6.7.3,第 121 页 here)可用于(如果您的编译器支持)声明要以原子方式读取和写入的变量,因此您可以例如这样做以下:

    static _Atomic float static_raw;
    
    float get_raw(void) {
        return static_raw;
    }
    

    如果这样做,请不要忘记使用-std=c11 进行编译。


    解决您的第一个编辑问题:

    当 foo 函数被调用时,它是否应该被一个互斥体包围,即使它只需要一个值的副本,也要将其锁定足够长的时间?

    虽然它是正确的,但它肯定不是最好的解决方案。如果函数只需要变量的副本,那么您的第二个 sn-p 无疑要好得多,并且应该是理想的解决方案:

    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. 锁定在 getter 内:

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

      此时,您甚至不需要 getter,您可以这样做:

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

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

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

    不可能提前说出最佳解决方案是什么,您应该运行不同的测试,看看哪种方案最适合您的方案。

    【讨论】:

    • 我收到了你的评论,但假设我不能使用 -std=c11 我这样做对吗?
    • @Luigi 很好,是的,如果你不能使用_Atomic,除了像你正在做的那样手动操作之外别无他法。
    • 所以像这样的调用 foo(a,b,c) 也应该受到互斥锁的保护,因为 a,b,c 可以从另一个线程修改?即使我通过副本传递它们?
    • @Luigi err,不清楚你的意思是什么,从你的评论中我对 foo, a, b, c 一无所知。我只是在谈论您在问题中输入的代码。
    • 请查看问题中的两个编辑
    猜你喜欢
    • 1970-01-01
    • 2014-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-17
    • 2023-03-23
    相关资源
    最近更新 更多