繁体   English   中英

以下C函数是否是线程安全的?

[英]Is the following C function thread-safe?

我看到一个博客声明下面的代码是线程安全的,但条件count不在mutex会导致数据损坏; 如果两个线程同时检查count但在获取mutex之前检查count ,并且在争用之后获取mutex 当一个线程完成时,另一个线程会非常盲目地向数组添加一个值。

char arr[10];
int count=0;

int func(char ctr)
{
    int i=0;
    if(count >= sizeof(arr))
    {
        printf("\n No storage\n");
        return -1;
    }

     /* lock the mutex here*/

    arr[count] = ctr;
    count++;

    /* unlock the mutex here*/

    return count;
}

如果我做出以下更改,我会是对的吗? 或者有更好/更有效的方法来做到这一点

   int func(char ctr)
    {
    int i=0;

    /* lock the mutex here*/

    if(count >= sizeof(arr))
    {
        printf("\n No storage\n");

        /* unlock the mutex here*/

        return -1;
    }


    arr[count] = ctr;
    count++;

    /* unlock the mutex here*/

    return count;
}`

你是对的。 通过在关键部分之外进行检查,您可以打开可能的缓冲区溢出的大门。 但请注意,返回的计数可能与用于存储ctr的索引不同。 即使在更正的代码中,这也是一个问题。

为了弥补你可以像这样重写它:

int func(char ctr)
{
    /* lock the mutex here*/

    if(count >= sizeof(arr)) {
        printf("\n No storage\n");

        /* unlock the mutex here*/

        return -1;
    }


    arr[count] = ctr;
    int c = count++;

    /* unlock the mutex here*/

    return c;
}

值得注意的是,如果这是唯一改变“count”的函数,那么没有两个线程能够在arr中改变相同的内存位置,这实际上是安全的:

int func(char ctr)
{
    /* lock the mutex here*/

    if(count >= sizeof(arr)) {
        printf("\n No storage\n");

        /* unlock the mutex here*/

        return -1;
    }

    int c = count++;

    /* unlock the mutex here*/

    arr[c] = ctr;

    return c;
}

如果这是模式,也许您可​​以将该代码重构为两个函数,如下所示:

int get_sequence(void)
{
    /* lock the mutex here*/

    int c = count++;

    /* unlock the mutex here*/

    return c;
}

int func(char ctr)
{
    int c = get_sequence();
    if(c >= sizeof(arr)) {
        printf("\n No storage\n");
        return -1;
    }

    arr[c] = ctr;

    return c;
}

请注意,只有get_sequence确实是唯一更改计数变量的函数时才会起作用。

首先,你是正确的,博客中的代码有可能超出数组的末尾。 限制检查仅在获取互斥锁后完成时才有效。

这是我写这个函数的方法:

bool func(char ctr)
{
    bool result;

    /* lock the mutex here */

    if (count >= sizeof(arr))
    {
        result = FAILURE;
    }
    else
    {
        arr[count] = ctr;
        count++;
        result = SUCCESS;
    }

    /* unlock the mutex here */

    if ( result == FAILURE )
        printf("\n No storage\n");

    return result;
}

这段代码的功能值得注意

  • 互斥锁定和解锁在函数中只出现一次,并且临界区中没有return语句。 这清楚地表明互斥锁将始终处于解锁状态。
  • printf位于临界区之外。 printf相对较慢,任何使用互斥锁的函数都应该在尽可能短的时间内保存互斥锁。
  • IMO函数不应该返回计数,而应该只返回指示成功或失败的bool 任何需要知道数组中有多少条目的代码都应该锁定互斥锁并直接检查计数。

以前的答案没有错,但有更好的方法。 不需要互斥锁。

int func(char ctr) {
    int c = interlocked_increment(&count);
    if (c >= sizeof(arr)) {
        printf("\n No storage\n");
        interlocked_decrement(&count);
        return -1;
    }
    arr[c-1] = ctr;
    return c;
}

这取决于互锁增量和减量功能的可用性,这些功能必须由您的操作系统或第三方库提供。


范围内的每个c值都保证是任何其他线程都看不到的值,因此是arr的有效插槽,如果有可用的话,没有线程会错过插槽。 存储值的顺序是不确定的,但大多数其他解决方案也是如此。 如果许多线程竞争存储,则由count的最大值也是不确定的,如果这是一个问题,则可能需要不同的方法。 如果count减少的行为是另一个未知的。 这个东西很难,并且总是可以添加额外的约束来使其更难。


只是要指出,还有另一种基于CSET(检查和设置)功能的可能实现。 这是一个函数,用于检查某个位置是否等于某个值,如果是,则以原子方式将其设置为另一个值,如果是则返回true。 它避免了对上述功能的一些批评。

int func(char ctr) {
    for (;;) {
      int c = count;
      if (c >= sizeof(arr)) {
        printf("\n No storage\n");
        return -1;
      }
      if (CSET(&count, c, c+1)) {
        arr[c] = ctr;
        return c;
      }
  }
}

C ++标准原子操作库包含一组atomic_compare_exchange函数,如果可用,它们应该用于此目的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM