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

时间:2014-08-27 02:51:06

标签: c multithreading

我看到一篇博客声明下面的代码是线程安全的,但条件count不在mutex内会导致数据损坏;如果两个线程同时检查count但在获得mutex之前检查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;
}`

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

{{1}}

3 个答案:

答案 0 :(得分:4)

你是对的。通过在关键部分之外进行检查,您可以打开可能的缓冲区溢出的大门。但请注意,返回的计数可能与用于存储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确实是改变计数变量的唯一函数时才会起作用。

答案 1 :(得分:2)

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

以下是我编写函数的方法:

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是 相对较慢,任何使用互斥锁的函数都应该保持 mutex尽可能少的时间。
  • IMO函数不应该返回计数,而应该只返回 返回bool表示成功或失败。任何需要的代码 要知道数组中有多少条目应该锁定互斥锁和 直接检查计数。

答案 2 :(得分:0)

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

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函数,如果可用,它们应该用于此目的。