【发布时间】:2013-11-26 21:28:07
【问题描述】:
我正在使用 pthreads 来尝试并行化 Dijkstra 的寻路算法,但我遇到了我似乎无法弄清楚的死锁场景。它的要点是每个线程都有自己的优先级队列,它可以在其中工作(std::multiset)和一个与该队列相对应的互斥锁,该队列在需要修改时被锁定。
每个节点都有一个所有者线程,它对应于节点 ID 模线程数。如果线程正在查看节点的邻居并将它们的权重(标签)之一更新为低于以前的值,它会锁定其所有者的队列并删除/重新插入(这是强制集合更新其在队列中的位置) .然而,这个实现似乎陷入了僵局。我不知道为什么,因为据我所知,每个线程一次只持有一个锁。
每个线程的初始队列都包含它的所有节点,但是除了源之外的每个节点的权重都被初始化为 ULONG_MAX。如果一个线程不工作(它从队列中获取具有 ULONG_MAX 权重的节点),它只会保持锁定和解锁,直到另一个线程让它工作。
void *Dijkstra_local_owner_worker(void *param){
struct thread_args *myargs = ((struct thread_args *)param);
int tid = myargs->tid;
std::multiset<Node *,cmp_label> *Q = (myargs->Q);
struct thread_args *allargs = ((struct thread_args *)param)-tid;
AdjGraph *G = (AdjGraph *)allargs[thread_count].Q;
struct Node *n, *p;
int owner;
std::set<Edge>::iterator it;
Edge e;
pthread_mutex_lock(&myargs->mutex);
while(!Q->empty()){
n = *Q->begin(); Q->erase(Q->begin());
pthread_mutex_unlock(&myargs->mutex);
if(n->label == ULONG_MAX){
pthread_mutex_lock(&myargs->mutex);
Q->insert(n);
continue;
}
for( it = n->edges->begin(); it != n->edges->end(); it++){
e = *it;
p = G->getNode(e.dst);
owner = (int)(p->index % thread_count);
if(p->label > n->label + e.weight){
pthread_mutex_lock(&(allargs[owner].mutex));
allargs[owner].Q->erase(p);
p->label = n->label + e.weight;
p->prev = n;
allargs[owner].Q->insert(p);//update p's position in the PQ
pthread_mutex_unlock(&(allargs[owner].mutex));
}
}
pthread_mutex_lock(&myargs->mutex);
}
pthread_mutex_unlock(&myargs->mutex);
return NULL;
}
这是产生线程的函数。
bool Dijkstra_local_owner(AdjGraph *G, struct Node *src){
G->setAllNodeLabels(ULONG_MAX);
struct thread_args args[thread_count+1];
src->label = 0;
struct Node *n;
for(int i=0; i<thread_count; i++){
args[i].Q = new std::multiset<Node *,cmp_label>;
args[i].tid = i;
pthread_mutex_init(&args[i].mutex,NULL);
}
for(unsigned long i = 0; i < G->n; i++){
n = G->getNode(i); //give all threads their workload in advance
args[(n->index)%thread_count].Q->insert(n);
}
args[thread_count].Q = (std::multiset<Node *,cmp_label> *)G;
//hacky repackaging of a pointer to prevent use of globals
//please note this works and is not the issue. I know it's horrible.
pthread_t threads[thread_count];
for(int i=0; i< thread_count; i++){
pthread_create(&threads[i],NULL,Dijkstra_local_owner_worker,&args[i]);
}
for(int i=0; i< thread_count; i++){
pthread_join(threads[i],NULL);
}
for(int i=0; i< thread_count; i++){
delete args[i].Q;
}
}
每个线程参数的结构定义:
struct thread_args{
std::multiset<Node *,cmp_label> *Q;
pthread_mutex_t mutex;
int tid;
};
我的问题是,这段代码死锁在哪里?我在这里得到了隧道视野,所以我看不出哪里出错了。我已经确保所有其他逻辑都正常工作,所以指针取消引用等都是正确的。
【问题讨论】:
-
获取核心转储并查看堆栈跟踪!如果它确实是死锁的,你应该找到两个相互持有对方锁的线程。
-
我没有看到标记节点 (
p->label = n->label + e.weight;) 和读取节点标签 (if (n->label == ULONG_MAX)) 之间的任何同步。也就是说,程序具有未定义的行为。我要做的第一件事是确保从分配它们的对象的析构函数中自动释放锁 - 这样可以保证不只是简单的泄漏。
标签: c++ multithreading pthreads deadlock dijkstra