简体   繁体   English

优先队列与 pthreads 同步

[英]Priority Queue synchronization with pthreads

I'm working on a college assignment where we are to implement parallelized A* search for a 15 puzzle .我正在完成一项大学作业,我们将在其中实施并行 A* 搜索15 谜题 For this part, we are to use only one priority queue (I suppose to see that the contention by multiple threads would limit speedup).对于这一部分,我们将只使用一个优先级队列(我想看到多个线程的争用会限制加速)。 A problem I am facing is properly synchronizing popping the next "candidate" from the priority queue.我面临的一个问题是正确同步从优先级队列中弹出下一个“候选人”。

I tried the following:我尝试了以下方法:

while(1) {
  // The board I'm trying to pop.
  Board current_board;

  pthread_mutex_lock(&priority_queue_lock);
  // If the heap is empty, wait till another thread adds new candidates.
  if (pq->heap_size == 0)
  {
    printf("Waiting...\n");
    pthread_mutex_unlock(&priority_queue_lock);
    continue;
  }
  current_board = top(pq);
  pthread_mutex_unlock(&priority_queue_lock);

  // Generate the new boards from the current one and add to the heap...
}

I've tried different variants of the same idea, but for some reason there are occasions where the threads get stuck on "Waiting".我尝试过相同想法的不同变体,但由于某种原因,有时线程会卡在“等待”中。 The code works fine serially (or with two threads), so that leads me to believe this is the offending part of the code.该代码可以串行(或使用两个线程)正常工作,因此让我相信这是代码的违规部分。 I can post the entire thing if necessary.如有必要,我可以发布整个内容。 I feel like it's an issue with my understanding of the mutex lock though.不过,我觉得这是我对互斥锁的理解的问题。 Thanks in advance for help.提前感谢您的帮助。

Edit: I've added the full code for the parallel thread below:编辑:我在下面添加了并行线程的完整代码:

// h and p are global pointers initialized in main()
void* parallelThread(void* arg)
{
    int thread_id = (int)(long long)(arg);
    while(1)
    {
        Board current_board;

        pthread_mutex_lock(&priority_queue_lock);
        current_board = top(p);
        pthread_mutex_unlock(&priority_queue_lock);

        // Move blank up.
        if (current_board.blank_x > 0)
        {
            int newpos = current_board.blank_x - 1;
            Board new_board = current_board;
            new_board.board[current_board.blank_x][current_board.blank_y] = new_board.board[newpos][current_board.blank_y];
            new_board.board[newpos][current_board.blank_y] = BLANK;
            new_board.blank_x = newpos;

            new_board.goodness = get_goodness(new_board.board);
            new_board.turncount++;

            if (check_solved(new_board))
            {
                printf("Solved in %d turns",new_board.turncount);
                exit(0);
            }

            if (!exists(h,new_board))
            {
                insert(h,new_board);
                push(p,new_board);
            }
        }

        // Move blank down.
        if (current_board.blank_x < 3)
        {
            int newpos = current_board.blank_x + 1;
            Board new_board = current_board;
            new_board.board[current_board.blank_x][current_board.blank_y] = new_board.board[newpos][current_board.blank_y];
            new_board.board[newpos][current_board.blank_y] = BLANK;
            new_board.blank_x = newpos;

            new_board.goodness = get_goodness(new_board.board);
            new_board.turncount++;

            if (check_solved(new_board))
            {
                printf("Solved in %d turns",new_board.turncount);
                exit(0);
            }

            if (!exists(h,new_board))
            {
                insert(h,new_board);
                push(p,new_board);
            }
        }

        // Move blank right.
        if (current_board.blank_y < 3)
        {
            int newpos = current_board.blank_y + 1;
            Board new_board = current_board;
            new_board.board[current_board.blank_x][current_board.blank_y] = new_board.board[current_board.blank_x][newpos];
            new_board.board[current_board.blank_x][newpos] = BLANK;
            new_board.blank_y = newpos;

            new_board.goodness = get_goodness(new_board.board);
            new_board.turncount++;

            if (check_solved(new_board))
            {
                printf("Solved in %d turns",new_board.turncount);
                exit(0);
            }

            if (!exists(h,new_board))
            {
                insert(h,new_board);
                push(p,new_board);
            }
        }

        // Move blank left.
        if (current_board.blank_y > 0)
        {
            int newpos = current_board.blank_y - 1;
            Board new_board = current_board;
            new_board.board[current_board.blank_x][current_board.blank_y] = new_board.board[current_board.blank_x][newpos];
            new_board.board[current_board.blank_x][newpos] = BLANK;
            new_board.blank_y = newpos;

            new_board.goodness = get_goodness(new_board.board);
            new_board.turncount++;

            if (check_solved(new_board))
            {
                printf("Solved in %d turns",new_board.turncount);
                exit(0);
            }

            if (!exists(h,new_board))
            {
                insert(h,new_board);
                push(p,new_board);
            }
        }
    }

    return NULL;
}

I tried the following:我尝试了以下方法:

I don't see anything wrong with the code that follows, assuming that top also removes the board from the queue.我看不出后面的代码有什么问题,假设top也从队列中删除了板。 It's wasteful (if the queue is empty, it will spin locking and unlocking the mutex), but not wrong.这很浪费(如果队列为空,它将旋转锁定和解锁互斥锁),但没有错。

I've added the full code我已经添加了完整的代码

This is useless without the code for exists , insert and push .如果没有existsinsertpush的代码,这是没有用的。

One general observation:一项一般性观察:

    pthread_mutex_lock(&priority_queue_lock);
    current_board = top(p);
    pthread_mutex_unlock(&priority_queue_lock);

In the code above, your locking is "ouside" of the top function.在上面的代码中,您的锁定是top function 的“外部”。 But here:但在这儿:

        if (!exists(h,new_board))
        {
            insert(h,new_board);
            push(p,new_board);
        }

you either do no locking at all (in which case that's a bug), or you do locking "inside" exists , insert and push .你要么根本不锁定(在这种情况下这是一个错误),要么你锁定“内部” existsinsertpush

You should not mix "inside" and "outside" locking.您不应该混合“内部”和“外部”锁定。 Pick one or the other and stick with it.选择其中一个并坚持下去。

If you in fact do not lock the queue inside exists , insert , etc. then you have a data race and are thinking of mutexes incorrectly: they protect invariants , and you can't check whether the queue is empty in parallel with another thread executing "remove top element" -- these operations require serialization, and thus must both be done under a lock.如果您实际上没有将队列锁定在existsinsert等内部,那么您有数据竞争并且错误地考虑了互斥锁:它们保护不变量,并且您无法检查队列是否与另一个线程并行执行“删除顶部元素”——这些操作需要序列化,因此都必须在锁定下完成。

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

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