线程锁模拟交叉点

时间:2012-11-26 21:56:07

标签: c multithreading pthreads

我正在尝试使用线程和互斥锁来模拟交集。

我有去海峡的功能,左转,右转。 现在,我有一个接近十字路口的功能。这会产生随机方向并转向。每个线程共享即将到来的交叉点。

我为所有方向的所有车辆定义了所有锁。

采取去海峡的功能。它有一个switch语句,可以打印当时汽车正在做什么。现在,我只是不确定锁定此功能的内容。如果汽车指向北方的方向,我会向东和向西锁定,同时汽车指向南向北走吗?

这是我的锁只调用锁定或解锁的功能

#define NUMCARS 30

#define lock_NW(CAR) lock(CAR, NW_mutex)
#define lock_NE(CAR) lock(CAR, NE_mutex)
#define lock_SW(CAR) lock(CAR, SW_mutex)
#define lock_SE(CAR) lock(CAR, SE_mutex)

#define unlock_NW(CAR) unlock(CAR, NW_mutex)
#define unlock_NE(CAR) unlock(CAR, NE_mutex)
#define unlock_SW(CAR) unlock(CAR, SW_mutex)
#define unlock_SE(CAR) unlock(CAR, SE_mutex)

这里是主要的

int main(int argc, char **argv){
/* Initial variables*/
int index, tid;
unsigned int carids[NUMCARS];
pthread_t carthreads[NUMCARS];

/* Start up a thread for each car*/ 
for(index = 0; index <NUMCARS; index++){
carids[index] = index;
tid = pthread_create(&carthreads[index], NULL, approachintersection,  (void*)&carids[index]);
}

/* Wait for every car thread to finish */
for(index = 0; index <NUMCARS; index++){
pthread_join(carthreads[index], NULL);
}
printf("Done\n");
return 1;
}

这是接近的交叉点,它调用函数strait

static void * approachintersection(void* arg){
unsigned int * carnumberptr;
unsigned int carnumber;
orientation_t cardir = (orientation_t)random()%4;
unsigned long turn = random()%3;

carnumberptr = (unsigned int*) arg;
carnumber = (unsigned int) *carnumberptr;

if(turn==LEFT){
turnleft(cardir, carnumber);
} else if(turn==RIGHT){
turnright(cardir, carnumber);
} else {//straight
gostraight(cardir, carnumber);
}

return (void*)carnumberptr;
}

现在,这是我要锁定适当方向的海峡功能。

 /*
  cardirection - The direction the car is pointing.  If it is pointing NORTH,
  it is starting from the South-Eastern corner of the intersection
  and "going straight" means it wants to move SOUTH to NORTH.

  valid options: NORTH, SOUTH, EAST, WEST

 carnumber -    The car identifier
*/


static void gostraight(orientation_t cardirection, unsigned int carnumber){

switch(cardirection){
case NORTH:
printf("Car %d, Moving South-North\n", carnumber);
break;
case SOUTH:
printf("Car %d, Moving North-South\n", carnumber);
break;
case EAST:
printf("Car %d, Moving West-East\n", carnumber);
break;
case WEST:
printf("Car %d, Moving East-West\n", carnumber);
break;
}
}

所以,如果接近的汽车从南方指向北方,那么汽车将是SE汽车,我会用lock_SE(CAR)锁定箱子东,西打印功能?防止其他线程进入并打印?所以我会解锁打印报表吗?

或者我会锁定整个switch语句吗?

**编辑:这会是这样做的吗? **

static void turnleft(orientation_t cardirection, unsigned int carnumber){

int CAR;
CAR = carnumber;


  switch(cardirection){
  case NORTH:
  lock_SE(CAR)
  printf("Car %d, Moving South-West\n", carnumber);
  unlock_SE(CAR)
  break;
  case SOUTH:
  lock_NW(CAR)
  printf("Car %d, Moving North-East\n", carnumber);
  unlock_NW(CAR)
  break;
  case EAST:
  lock_SW(CAR)
  printf("Car %d, Moving West-North\n", carnumber);
  unlock_SW(CAR)
  break;
  case WEST:
  lock_NE(CAR)
  printf("Car %d, Moving East-South\n", carnumber);
  unlock_NE(CAR)
  break;
  }

}

3 个答案:

答案 0 :(得分:2)

这不是一个容易的问题。我将尝试展示两种解决方案。

首先显而易见的一个:整个交集的一个互斥,在turnleft, turnright, gostraight添加lock(car, intersection_mutex);的开头,就在每个函数发布结束之前说mutex。这只会让一辆汽车一次穿过十字路口。这样做的好处是它易于理解,不会导致死锁。缺点是一次只能有一辆车进入,但众所周知,两辆行驶非交叉路径的车可以顺利进入。 以下是go_straight()的示例(其他人采用相同的方法):

static void gostraight(orientation_t cardirection, unsigned int carnumber){
    pthread_mutex_lock(&intersection_mutex);
    switch(cardirection){
        case NORTH:
            printf("Car %d, Moving South-North\n", carnumber);
            break;
        case SOUTH:
            printf("Car %d, Moving North-South\n", carnumber);
            break;
        case EAST:
            printf("Car %d, Moving West-East\n", carnumber);
            break;
        case WEST:
            printf("Car %d, Moving East-West\n", carnumber);
            break;
        }
    }
    pthread_mutex_unlock(&intersection_mutex);
}

为了让我们不止一辆车进入,我们需要一个细粒度的方法。细粒度方法的问题在于它难以实现并且变得更加正确。 go_straightturn_left都需要锁定两个互斥锁(你可以说左转需要三个......)。因此,如果您无法获得这两种互斥锁,则需要退出。将其转化为驾驶规则:

you must not enter the intersection before you can exit it. 

所以,要直截了当我们必须首先获得离您最近的互斥锁,然后是您路径中的下一个可以退出的互斥锁。如果我们无法获得两者,我们必须释放我们锁定的那个。如果我们不发布它,我们就会死锁。

要做到这一点,我要添加两个辅助函数:

static void lock_two(pthread_mutex_t *a, pthread_mutex_t *b) {
    while(1) { 
        pthread_mutex_lock(a);
        if(pthread_mutex_trylock(b) == 0) 
            break;
        else
        /* We must release the previously taken mutex so we don't dead lock the intersection */
            pthread_mutex_unlock(a);                            
        pthread_yield(); /* so we don't spin over lock/try-lock failed */
    }
}
static void unlock_two(pthread_mutex_t *a, pthread_mutex_t *b) {
    pthread_mutex_unlock(a);
    pthread_mutex_unlock(b);
}

这是我的直接版本:

static void gostraight(orientation_t cardirection, unsigned int carnumber){  
    switch(cardirection){
        case NORTH:
            lock_two(&SE_mutex, &NE_mutex); 
            printf("Car %d, Moving South-North\n", carnumber);
            unlock_two(&SE_mutex, &NE_mutex); 
            break;
        case SOUTH:
            lock_two(&NW_mutex, &SW_mutex); 
            printf("Car %d, Moving North-South\n", carnumber);
            unlock_two(&NW_mutex, &SW_mutex); 
            break;
        case EAST:
            lock_two(&SW_mutex, &SE_mutex); 
            printf("Car %d, Moving West-East\n", carnumber);
            unlock_two(&SW_mutex, &SE_mutex); 
       break;
       case WEST:
            lock_two(&NE_mutex, &NW_mutex); 
            printf("Car %d, Moving East-West\n", carnumber);
            unlock_two(&NE_mutex, &NW_mutex); 
            break;
    }
}
然后

turn_left需要遵循相同的方法。

答案 1 :(得分:1)

这样的东西适用于直接功能: -

static void gostraight(orientation_t cardirection, unsigned int carnumber){
  int CAR;
  cAR = carnumber;

  switch(cardirection){
    case NORTH:
      lock_SE(CAR);
      lock_NE(CAR);
      printf("Car %d, Moving South-North\n", carnumber);
      unlock_NE(CAR);
      unlock_SE(CAR);
      break;
    case SOUTH:
      lock_NW(CAR);
      lock_SW(CAR);
      printf("Car %d, Moving North-South\n", carnumber);
      unlock_SW(CAR);
      unlock_NW(CAR);
      break;
    case EAST:
      lock_SE(CAR);
      lock_SW(CAR);
      printf("Car %d, Moving West-East\n", carnumber);
      unlock_SE(CAR);
      unlock_SW(CAR);
      break;
    case WEST:
      lock_NE(CAR);
      lock_NW(CAR);
      printf("Car %d, Moving East-West\n", carnumber);
      unlock_NW(CAR);
      unlock_NE(CAR);
      break;
  }
}

左转,(仅举一个例子): -

switch(cardirection) {
  case NORTH:
    lock_SE(CAR);
    lock_NE(CAR);
    lock_NW(CAR);
    printf("Car %d, Moving South-West\n", carnumber);
    unlock_NW(CAR);
    unlock_NE(CAR);
    unlock_SE(CAR);
    break;
}

右转(再次只是一个例子): -

switch(cardirection) {
  case EAST:
    lock_SW(CAR);
    printf("Car %d, Moving East-South\n", carnumber);
    unlock_SW(CAR);
    break;
}

请注意,为了避免死锁,我总是以SE,NE,NW,SW的任意顺序锁定它们。

为什么会这样?例如,乘坐一辆想要向南直行的汽车和一辆向北行驶并向左转向西的汽车。 然后,如果左转车首先出现,它将按顺序锁定SE,NE和NW,直行车将无法锁定NW。然而,一辆向东行驶并从右向南行驶的汽车将能够锁定SW。

因此,这个方案将起作用。它不会有死锁,因为每个函数都以给定的顺序获取锁。因此,永远不会有循环的线程链竞争角落。基本上,如果线程1正在等待线程2释放锁,那么这意味着线程2不需要线程1已经获得的任何锁。因此,不存在死锁。

答案 2 :(得分:0)

我不太清楚我明白你需要做什么,但我会尝试解释我的推理。我将从设计开始,因为我猜这就是你的问题所在。

你假设一个平坦的十字路口,两条道路,没有灯,没有标志。任何汽车都可以从北,南,东,西四个不同方向到达交叉路口,每辆车在经过十字路口时可以从三个不同的方向中选择一个。 这样,您就必须考虑4 * 3 = 12个不同的路段,这些路段都不同。

如果汽车在某个时刻穿过某条路径的交叉路口,它会阻止零点或更多的交通,理论上最多11个不同的路段(实际上,至少有两个路段保持空闲,因此限制为9) 。这些你必须阻止。顺便说一下,如果你拍摄12个不同的部分,它会有所帮助。

如果您想使用互斥锁,则每个部分都需要一个(不一定是汽车)。假设有一个右手,左前方的驾驶场景,一辆从南方来的车将会阻挡:

  • 一辆来自东方的车直行;
  • 一辆来自东方的车左转;
  • 来自东方的汽车右转;
  • 来自西方的汽车直奔;
  • 一辆来自西方的汽车向左走;
  • 一辆来自北方的车左转;

所有其他部分对其他车辆免费。您可以为每个到达/方向对创建此块列表。 [你必须说明两辆来自相反方向的左转车辆如何相互通过,即从北向左转的车是否会阻挡从南向左转的车辆,或者是否可以在彼此之前通过的车辆)< / p>

代表每辆汽车(读取:线程)阻塞这么多互斥锁(每个部分一个)会出现问题:死锁。为了解决这个问题,我建议你为整个交叉点创建一个额外的“主”互斥体。 当一辆车到达时,

  1. 它试图阻止主互斥锁;如果不成功,它会阻止。
  2. 然后尝试阻止它需要的每个节 - 互斥体;如果不成功,请释放所有成功锁定的互斥锁,解锁主互斥锁,然后等待,然后从1开始重试。
  3. 如果成功阻止了所有需要的部分互斥锁,请解锁主互斥锁,然后通过交叉点(需要时间)。
  4. 经过十字路口后,车子

    1. 试图阻止主互斥锁;如果不成功,它会阻止。
    2. 解锁所有获得的部分互斥锁。
    3. 它再次解锁主互斥锁。
    4. 继续沿着新方向前进。
    5. [编辑澄清,并且pthread互斥体允许尝试锁定和退出] 主互斥体总是很快释放,仅用于查看汽车是否可以获得所有必要的部分互斥锁。无论哪种方式,主互斥体都会立即释放。

      如果没有主互斥锁,只能通过使用严格的顺序获取互斥锁来避免死锁。这将使交叉点部分:如果两辆车同时到达,则一个方向/转弯组合将总是胜过另一个。主互斥体可以避免这种情况。