我看到一篇博客声明下面的代码是线程安全的,但条件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}}
答案 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尽可能少的时间。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函数,如果可用,它们应该用于此目的。