我正在尝试使用线程和互斥锁来模拟交集。
我有去海峡的功能,左转,右转。 现在,我有一个接近十字路口的功能。这会产生随机方向并转向。每个线程共享即将到来的交叉点。
我为所有方向的所有车辆定义了所有锁。
采取去海峡的功能。它有一个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;
}
}
答案 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_straight
和turn_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>
代表每辆汽车(读取:线程)阻塞这么多互斥锁(每个部分一个)会出现问题:死锁。为了解决这个问题,我建议你为整个交叉点创建一个额外的“主”互斥体。 当一辆车到达时,
经过十字路口后,车子
[编辑澄清,并且pthread互斥体允许尝试锁定和退出] 主互斥体总是很快释放,仅用于查看汽车是否可以获得所有必要的部分互斥锁。无论哪种方式,主互斥体都会立即释放。
如果没有主互斥锁,只能通过使用严格的顺序获取互斥锁来避免死锁。这将使交叉点部分:如果两辆车同时到达,则一个方向/转弯组合将总是胜过另一个。主互斥体可以避免这种情况。