简体   繁体   English

线程锁模拟交叉点

[英]Thread locks simulating an intersection

I am trying to simulate an intersection using threads and mutex locks. 我试图使用线程和互斥锁来模拟交集。

I have functions for going strait, turn left, turn right. 我有去海峡的功能,左转,右转。 Now, i have a function for approaching the intersection. 现在,我有一个接近十字路口的功能。 This generates a random orientation and turn. 这会产生随机方向并转向。 Each thread shares the approaching intersection. 每个线程共享接近的交叉点。

I have all the locks defined for all the cars in all directions. 我为所有方向的所有汽车定义了所有锁。

Take the going strait function. 采取进入海峡的功能。 It has a switch statement that just prints what car is doing what at the time. 它有一个switch语句,可以打印当时汽车正在做什么。 Now, i am just not sure what to lock in this function. 现在,我只是不确定锁定此功能的内容。 If the car is in direction pointing north would i lock east and west and same with the car pointing south going north? 如果汽车指向北方的方向,我将锁定东西方向,同时指向南方向北的汽车?

Here is my locks which just calls a function to lock or unlock 这是我的锁只调用一个锁定或解锁的功能

#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)

here is main 这里是主要的

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;
}

here is the approaching intersection which calls the function going 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;
}

Now, here is the going strait function where i want to lock the appropriate directions. 现在,这是正在进行的海峡功能,我想锁定适当的方向。

 /*
  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;
}
}

So, if the approaching car is pointing north from south the car would be the SE car and i would lock case east, west print function with lock_SE(CAR)? 因此,如果接近的汽车从南方指向北方,那么汽车将是SE汽车,我会用lock_SE(CAR)锁定箱子东,西打印功能? preventing the other threads from coming in and printing? 防止其他线程进入并打印? so i would lock unlock the print statements? 所以我会锁定解锁打印语句?

Or would i lock the whole switch statement? 或者我会锁定整个switch语句?

** EDIT: would this be the way to do it? **编辑:这会是这样做的吗? ** **

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;
  }

} }

This is not an easy problem. 这不是一个容易的问题。 I'll try to show two solutions. 我将尝试展示两种解决方案。

First the obvious one: One mutex for the entire intersection, in the beginning of turnleft, turnright, gostraight add lock(car, intersection_mutex); 首先是明显的一个:整个交叉路口的一个互斥体,在turnleft, turnright, gostraight开始时turnleft, turnright, gostraight添加lock(car, intersection_mutex); , just before the end of each function release said mutex. ,就在每个函数发布结束之前说mutex。 This will only let one car in through the intersection at a time. 这只会让一辆汽车一次穿过十字路口。 The upside of this is that it's easy to understand and will not result in dead locks. 这样做的好处是它易于理解,不会导致死锁。 The downside is that a only one car can enter at a time but as we all know two cars travelling non-intersecting paths can enter without issues. 缺点是一次只能有一辆车进入,但众所周知,两辆行驶非交叉路径的车可以顺利进入。 Here's an example of go_straight() (the others follow the same approach): 这是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);
}

To let more than one car in at a time we need a fine grained approach. 为了让我们不止一辆车进入,我们需要一个细粒度的方法。 The problem with a fine grained approach is that it's much harder to implement and get correct. 细粒度方法的问题在于它更难实现并且变得更加正确。 Both go_straight and turn_left needs to lock two mutexes (you could argue that turn left needs three..). go_straightturn_left需要锁定两个互斥锁(你可以说左转需要三个......)。 So if you can't acquire both mutexes you need to back off. 因此,如果您无法获得这两个互斥锁,则需要退出。 Translating this into driving rules: 将其转化为驾驶规则:

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

So, to go straight we must first acquire the mutex nearest to you, then the next one in your path to be able to exit. 所以,要直截了当,我们必须首先获得离您最近的互斥锁,然后是您路径中的下一个可以退出的互斥锁。 If we can't get both we must release the one we have locked. 如果我们无法获得两者,我们必须释放我们锁定的那个。 If we don't release it we will dead-lock. 如果我们不释放它,我们将死锁。

To do this I'd add two helper functions: 为此,我将添加两个辅助函数:

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);
}

Here's my version of go straight: 这是我直接的版本:

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 would then need to follow the same approach. turn_left然后需要遵循相同的方法。

Well something like this would work for the going straight function :- 这样的东西可以用于直线功能: -

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;
  }
}

For the left turn, (just giving one example) :- 左转,(仅举一个例子): -

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;
}

For right turn (again just one example) :- 右转(再次只是一个例子): -

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

Note that in order to avoid deadlocks, I am always locking them in the arbitrary order of SE, NE, NW, SW. 请注意,为了避免死锁,我总是以SE,NE,NW,SW的任意顺序锁定它们。

Why this works? 为什么会这样? eg, take a car that wants to go straight south and another that is headed north and turning left to west. 例如,乘坐一辆想要向南直行的汽车和另一辆向北行驶并向左转向西的汽车。 Then if the left turning car comes first, it will lock SE, NE and NW in that order and the straight going car will not be able to get the lock on NW. 然后,如果左转车首先出现,它将按顺序锁定SE,NE和NW,直行车将无法锁定NW。 However, a car going east and turning right to south will be able to get the lock on SW. 然而,一辆向东行驶并从右向南行驶的汽车将能够锁定SW。

Hence, this scheme will work. 因此,这个方案将起作用。 It won't have a deadlock because each function obtains the locks in a given order. 它不会有死锁,因为每个函数都以给定的顺序获取锁。 Hence there can never be cyclic chain of threads competing for corners. 因此,永远不会有循环的线程链竞争角落。 Basically, if a thread 1 is waiting for a thread 2 to release a lock, then that means thread 2 does not require any of the locks already obtained by thread 1. Hence there can be no deadlock. 基本上,如果线程1正在等待线程2释放锁,那么这意味着线程2不需要线程1已经获得的任何锁。因此,不存在死锁。

I'm not quite sure I understand what you need to do, but I'll have a try at explaining my reasoning. 我不太清楚我明白你需要做什么,但我会尝试解释我的推理。 I'll start at the design, since I guess that's where your problem lies. 我将从设计开始,因为我猜这就是你的问题所在。

You assume a plain intersection, two roads, no lights, no signs. 你假设一个平坦的交叉路口,两条道路,没有灯,没有标志。 Any car can arrive at the intersection from four different directions, north, south, east and west, and each car can take one of three different directions when passing the intersection. 任何汽车都可以从北,南,东,西四个不同方向到达交叉路口,每辆车在经过十字路口时可以从三个不同的方向中选择一个。 This way, you have 4 * 3 = 12 different road sections you'd have to consider, all different. 这样,你有4 * 3 = 12不同的路段,你必须考虑,所有不同。

If a car crosses the intersection on a certain path at a certain moment, it blocks traffic at zero or more, theoretically up to 11, different sections (in practice, at least two sections remain free, so the limit is 9). 如果汽车在特定时刻穿过某条路径上的交叉路口,它会阻止零点或更多的交通,理论上最多11个不同的路段(实际上,至少有两个路段保持空闲,因此限制为9)。 These you have to block. 这些你必须阻止。 BTW, it helps if you make a picture of the 12 different sections. 顺便说一句,如果你拍摄12个不同的部分,它会有所帮助。

If you want to use mutexes, you'd need one for each section (not necessarily for a car). 如果你想使用互斥锁,你需要每个部分一个(不一定是汽车)。 Assuming a right hand, right-before-left driving scenario, a car coming from the south intending to go straight would be blocking: 假设一个右手,左前方的驾驶场景,一辆来自南方的汽车打算直行将会阻塞:

  • a car coming from the east going straight; 一辆来自东方的汽车直奔;
  • a car coming from the east going left; 一辆来自东方的汽车左转;
  • a car coming from the east going right; 一辆来自东方的汽车右转;
  • a car coming from the west going straight; 一辆来自西方的汽车直奔;
  • a car coming from the west going left; 一辆来自西方的汽车向左走;
  • a car coming from the north going left; 一辆来自北方的汽车左转;

All other sections are free for other cars. 所有其他部分对其他车辆免费。 You can create this list of blocks for every arrival/direction pair. 您可以为每个到达/方向对创建此块列表。 [You'd have to specify how two left-turning cars from opposite directions shall pass each other, ie whether a car coming from north turning left will block a car coming from south turning left, or whether they can pass before one another] [你必须说明两辆来自相反方向的左转车辆如何相互通过,即从北向左转弯的汽车是否会阻挡从南向左转的汽车,或者它们是否可以在彼此之前通过]

Blocking so many mutexes (one for each section) on behalf of each car (read: thread) presents a problem: Deadlock. 代表每辆汽车(读取:线程)阻塞这么多互斥锁(每个部分一个)会出现问题:死锁。 In order to solve that problem here, I'd suggest you create an additional, 'master' mutex for the whole intersection. 为了解决这个问题,我建议你为整个交叉点创建一个额外的“主”互斥体。 When a car arrives, 当一辆车到达时,

  1. it tries to block the master mutex; 它试图阻止主互斥体; if unsuccessful, it blocks. 如果不成功,它会阻止。
  2. it then tries to block each of the section-mutexes it needs; 然后它试图阻止它需要的每个部分互斥量; if unsuccessful, release all successfully locked mutexes, unlock the master mutex, and wait, then try again starting from 1. 如果不成功,请释放所有成功锁定的互斥锁,解锁主互斥锁,然后等待,然后从1开始重试。
  3. If it succeeded in blocking all needed section mutexes, unlock the master mutex, and pass the intersection (taking time). 如果它成功阻止了所有需要的部分互斥锁,则解锁主互斥锁,并传递交集(花费时间)。

After passing the intersection, the car 经过十字路口后,车

  1. tries to block the master mutex; 试图阻止主互斥; if unsuccessful, it blocks. 如果不成功,它会阻止。
  2. it unlocks all acquired section mutexes. 它解锁所有获得的部分互斥锁。
  3. it unlocks the master mutex again. 它再次解锁主互斥锁。
  4. continues its way in the new direction. 继续沿着新方向前进。

[Edited to clarify, and pthread mutexes allow trying to lock and backing off] The master mutex is always released very quickly, only used to see if a car can obtain all necessary section mutexes or not. [编辑澄清,并且pthread互斥体允许尝试锁定和后退]主互斥体总是非常快速地释放,仅用于查看汽车是否可以获得所有必要的部分互斥锁。 Either way, the master mutex is released immediately aftre that. 无论哪种方式,主互斥体立即释放。

Without a master mutex you can avoid deadlock only by using a strict order for obtaining the mutexes. 如果没有主互斥锁,只能通过使用严格的顺序获取互斥锁来避免死锁。 This would make the intersection partial: If two cars arrive at the same time, one direction/turn combination will always win over another one. 这将使交叉点部分:如果两辆车同时到达,则一个方向/转弯组合将总是胜过另一个。 The master mutex avoids that. 主互斥体可以避免这种情况。

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

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