面包结构在结构中使用时不起作用

时间:2012-02-10 23:28:52

标签: c multithreading locking pthreads

我是多线程编程的新手,我尝试在C中编写Bakery Lock Algorithm代码。

以下是代码:

int number[N];  // N is the number of threads                                                          
int choosing[N];                

void lock(int id)  {                                                                       
  choosing[id] = 1;                                                         
  number[id] = max(number, N) + 1;                                       
  choosing[id] = 0;                                                         

  for (int j = 0; j < N; j++)                                                   
    {                                                                       
      if (j == id)                                                          
        continue;                                                           

      while (1)                                                             
          if (choosing[j] == 0)                                             
            break;                                                          

      while (1)                                                             
        {                                                                   
          if (number[j] == 0)                                               
            break;                                                          
          if (number[j] > number[id]                                        
              || (number[j] == number[id] && j > id))                       
            break;                                                          
        }                         
    }
}

void unlock(int id)  {
   number[id] = 0;
}

然后我运行以下示例。我运行100个线程,每个线程运行以下代码:

  for (i = 0; i < 10; ++i)  {      
      lock(id);                                                                   
      counter++;
      unlock(id);                                              
    }                                                                       

执行完所有线程后,共享counter的结果为10 * 100 = 1000,这是预期值。我多次执行我的程序,结果总是1000。所以似乎锁的实现是正确的。基于previous question {I},这看起来很奇怪,因为我没有使用任何内存屏障/栅栏。 我是幸运吗

然后我想创建一个使用许多不同锁的多线程程序。所以我创建了这个(完整的代码可以找到here):

typedef struct {                                                            
  int number[N];                                                            
  int choosing[N];                                                          
} LOCK;      

并且代码更改为:

void lock(LOCK l, int id)                                                        
{                                                                           
  l.choosing[id] = 1;                                                                                                          
  l.number[id] = max(l.number, N) + 1;                                                                                            
  l.choosing[id] = 0;                 
...

现在,在执行我的计划时,有时我会997,有时会998,有时会1000。所以锁定算法不正确。

我做错了什么?我该怎么做才能解决它?

现在我正在从number读取数组choosingstruct,这可能是一个问题 那不是原子的东西吗?

我应该使用内存防护,如果是这样的话(我尝试在代码的各个方面使用asm("mfence"),但它没有帮助)?

1 个答案:

答案 0 :(得分:1)

使用pthreads,标准规定访问一个线程中的varable而另一个线程正在或可能正在修改它是未定义的行为。您的代码遍布整个地方。例如:

  while (1)                                                             
      if (choosing[j] == 0)                                             
        break;

此代码在等待另一个线程修改它时反复访问choosing[j]。编译器完全可以自由修改此代码,如下所示:

int cj=choosing[j];
while(1)
    if(cj == 0)
       break;

为什么呢?因为标准很清楚,当该线程可能正在访问它时,另一个线程可能无法修改该变量,因此可以假设该值保持不变。但显然,这不会起作用。

它也可以这样做:

while(1)
{
   int cj=choosing[j];
   if(cj==0) break;
   choosing[j]=cj;
}

相同的逻辑。编译器写回变量是否合法是完全合法的,只要它在代码可以访问变量时这样做。 (因为在那个时候,另一个线程修改它是不合法的,所以值必须相同而且写入是无害的。在某些情况下,写入确实是一个优化,现实世界的代码有被这些回写打破了。)

如果要编写自己的同步函数,则必须使用具有适当原子性和内存可见性语义的原始函数构建它们。您必须遵守规则,否则您的代码失败,并且会以可怕和不可预测的方式失败。