【问题标题】:Mutex can't acquire lock互斥锁无法获得锁
【发布时间】:2012-03-01 20:29:49
【问题描述】:

我有一个问题,我的一个函数无法获取我使用的 2 个互斥锁之一的锁定。 我在 VC++2010 中做了一个基本调试,设置了一些断点,似乎如果在任何地方获得了锁,它就会被解锁。

使用互斥体的代码如下:

#define SLEEP(x) { Sleep(x); }
#include<windows.h>

    void Thread::BackgroundCalculator( void *unused ){
    while( true ){
        if(MUTEX_LOCK(&mutex_q, 5) == 1){
            if(!QueueVector.empty()){
//cut
                MUTEX_UNLOCK(&mutex_q);
                    //cut
                while(MUTEX_LOCK(&mutex_p,90000) != 1){}
                //cut
                MUTEX_UNLOCK(&mutex_p);
            }

        }
        SLEEP(25);
    }
}

然后在其他地方:

PLUGIN_EXPORT void PLUGIN_CALL
    ProcessTick(){
    if(g_Ticked == g_TickMax){
        if(MUTEX_LOCK(&mutex_p, 1) == 1){
            if(!PassVector.empty()){
                PassVector.pop();
            }
            MUTEX_UNLOCK(&mutex_p);
        }
        g_Ticked = -1;
    }
    g_Ticked += 1;
}

static cell AMX_NATIVE_CALL n_CalculatePath( AMX* amx, cell* params ){
    if(MUTEX_LOCK(&mutex_q,1) == 1){
        QueueVector.push_back(QuedData(params[1],params[2],params[3],amx));
        MUTEX_UNLOCK(&mutex_q);
        return 1;
    }
    return 0;
}

初始化:

PLUGIN_EXPORT bool PLUGIN_CALL Load( void **ppData ) {
    MUTEX_INIT(&mutex_q);
    MUTEX_INIT(&mutex_p);
    START_THREAD( Thread::BackgroundCalculator, 0);
    return true;
}

一些变量和函数:

int MUTEX_INIT(MUTEX *mutex){
    *mutex = CreateMutex(0, FALSE, 0);
    return (*mutex==0);
}

int MUTEX_LOCK(MUTEX *mutex, int Timex = -1){
    if(WaitForSingleObject(*mutex, Timex) == WAIT_OBJECT_0){
        return 1;
    }
    return 0;
}
int MUTEX_UNLOCK(MUTEX *mutex){
    return ReleaseMutex(*mutex);
}

MUTEX mutex_q = NULL;
MUTEX mutex_p = NULL;

并定义:

#   include <process.h>
#   define OS_WINDOWS
#   define MUTEX HANDLE
#   include <Windows.h>
#   define EXIT_THREAD() { _endthread(); }
#   define START_THREAD(a, b) { _beginthread( a, 0, (void *)( b ) ); }

线程头文件:

#ifndef __THREAD_H
#define __THREAD_H

class Thread{
    public:
                                    Thread                      ( void );
                                   ~Thread                      ( void );
    static void                     BackgroundCalculator        ( void *unused );

};

#endif

好吧,我似乎找不到问题所在。 调试后,我想通过这段代码(来自 pawn 抽象机)“强制”获取锁:

if (strcmp("/routeme", cmdtext, true) == 0){
    new fromnode = NearestPlayerNode(playerid);
    new start = GetTickCount();
    while(CalculatePath(fromnode,14,playerid+100) == 0){
        printf("0 %d",fromnode);
    }
    printf("1 %d",fromnode);
    printf("Time: %d",GetTickCount()-start);
    return 1;
}

但它一直在不停地进行,CalculatePath 调用静态单元 AMX_NATIVE_CALL n_CalculatePath( AMX* amx, cell* params )

这有点意外。有没有人看错了?

如果您需要完整的源代码,请访问:

http://gpb.googlecode.com/files/RouteConnector_174alpha.zip

额外信息: PLUGIN_EXPORT bool PLUGIN_CALL 加载 仅在启动时执行。

静态单元 AMX_NATIVE_CALLs 仅在从虚拟机调用时执行

过程滴答() 在应用程序的每个进程滴答声中执行,在它完成自己的作业后,它在扩展中调用它。

目前我只在 windows 上测试了代码,但它在 linux 上编译得很好。

编辑:删除 linux 代码以缩短帖子。

【问题讨论】:

  • 代码太多了...你能把它缩减为更易于管理的代码吗?尝试删除代码,直到您无法再重现问题,并且当这种情况发生时,只需提交足以重现问题的代码。最好能提供一个符合sscce.org 的示例。
  • 我尽了最大的努力,几乎删除了所有与线程无关的东西。
  • 嗯,看起来这个问题在“未回答”列表中的排名很低? ; o

标签: c++ multithreading mutex


【解决方案1】:

从我看到你的第一个 sn-p 仅基于某些条件解锁互斥锁,即在伪代码中它是这样的:

mutex.lock ():
if some_unrelated_thing:
    mutex.unlock ()

据我了解您的代码,这样第一个 sn-p 原则上可以锁定然后永远不会解锁。

另一个潜在问题是您的代码最终是异常不安全的。您真的能够保证在锁定/解锁操作之间不会发生异常吗?因为如果任何未捕获的异常被抛出,你就会陷入描述的死锁。我建议在这里使用某种 RAII。

编辑:

执行锁定/解锁的未经测试的 RAII 方式:

struct Lock
{
  MUTEX&  mutex;
  bool    locked;

  Lock (MUTEX& mutex)
    : mutex (mutex),
      locked (false)
  { }

  ~Lock ()
  { release (); }

  bool acquire (int timeout = -1)
  {
    if (!locked && WaitForSingleObject (mutex, timeout) == WAIT_OBJECT_0)
      locked = true;
    return locked;
  }

  int release ()
  {
    if (locked)
      locked = ReleaseMutex (mutex);
    return !locked;
  }
};

用法可能是这样的:

{
  Lock  q (mutex_q);
  if (q.acquire (5)) {
      if (!QueueVector.empty ()) {
          q.release ();
          ...
      }
  }
}

请注意,这种方式~Lock 总是释放互斥锁,无论您是否明确地这样做,无论作用域块正常退出还是由于未捕获的异常。

【讨论】:

  • 确实是问题所在。有点可惜没看到。无论如何,赏金可以在 22 小时内提供,敬请期待.. ByTheWay 关于 RAII,你能举个例子(用我的代码吗?)
  • @Rafal Grasman:请参阅答案中的编辑。您也可以尝试使用 Boost,它还提供了可移植性并节省了编写(和测试)您自己的代码。
  • 啊现在我也明白了:] 谢谢。是的,很快我就会用 boost 重新制作我的项目。
  • 距离赏金还剩 10 小时,当解决方案被接受时等待有点愚蠢 =D
  • 我认为这是因为您可以针对您未提出的问题发布赏金,因此不能保证两者之间存在关联。
【解决方案2】:

我不确定这是否是预期行为,但在这段代码中:

void Thread::BackgroundCalculator( void *unused ){
while( true ){
    if(MUTEX_LOCK(&mutex_q, 5) == 1){
        if(!QueueVector.empty()){
            //cut
            MUTEX_UNLOCK(&mutex_q);
            //cut
            while(MUTEX_LOCK(&mutex_p,90000) != 1){}
            //cut
            MUTEX_UNLOCK(&mutex_p);
        }
    }
    SLEEP(25);
}

如果QueueVector.empty 为真,您将永远无法解锁mutex_q

【讨论】:

  • 是的,这就是我看到的有问题的代码,你之前的海报有点早,呵呵。
  • 是的,考虑到我立即看到了问题,他的速度非常快。 :)
猜你喜欢
  • 2018-05-23
  • 1970-01-01
  • 2010-09-16
  • 2010-12-17
  • 2018-11-12
  • 2017-05-05
  • 2015-10-26
  • 2022-07-31
  • 2012-06-05
相关资源
最近更新 更多