Windows 命名信号量未锁定

Posted

技术标签:

【中文标题】Windows 命名信号量未锁定【英文标题】:Windows Named Semaphore Not Getting Locked 【发布时间】:2017-12-01 21:00:34 【问题描述】:

我正在开发调用 Windows API C 库的 C++ 类。

我将信号量用于一项任务,假设我有两个进程:

ProcessA 有两个信号量:

全局\processA_receiving_semaphore

全局\processA_waiting_semaphore

ProcessB 有两个信号量:

全局\processB_receiving_semaphore

全局\processB_waiting_semaphore

我在每个进程中有两个线程:

在进程A中发送线程:

等待“Global\processB_waiting_semaphore”

// 做点什么

信号“全局\processB_receiving_semaphore”

processB 上的接收线程:

等待“Global\processB_receiving_semaphore”

// 做点什么

信号“全局\processB_waiting_semaphore”

我删除了所有发布“Global\processB_waiting_semaphore”的代码,但仍然可以获取它。在该信号量上调用WaitForSingleObject 总是会立即返回成功等待。我尝试将超时时间设置为 0,但它仍然获取信号量,而 NOTHING 正在释放它。

接收信号量有initial count = 0max count = 1,而等待信号量有initial count = 1max count = 1

在接收信号量上调用WaitForSingleObject 效果很好并且阻塞,直到它被另一个进程释放。问题出在等待信号量上,我不知道为什么。代码很大,我确保信号量的名称设置正确。

这是一个常见问题吗?如果您需要更多解释,请发表评论,我会修改帖子。


编辑:添加代码:

接收器信号量:

bool intr_process_comm::create_rcvr_semaphores()

  std::cout << "\n  Creating semaphore: " << "Global\\" << this_name << "_rcvr_sem";
  rcvr_sem = CreateSemaphore(NULL, 0, 1, ("Global\\" + this_name + "_rcvr_sem").c_str());

  std::cout << "\n  Creating semaphore: " << "Global\\" << this_name << "_wait_sem";
  wait_sem = CreateSemaphore(NULL, 1, 1, ("Global\\" + this_name + "_wait_sem").c_str());

  return (rcvr_sem && wait_sem);

发送者信号量:

// this sender connects to the wait semaphore in the target process
sndr_sem = OpenSemaphore(SEMAPHORE_MODIFY_STATE, FALSE, ("Global\\" + target_name + "_wait_sem").c_str());
// this target connects to the receiver semaphore in the target process
trgt_sem = OpenSemaphore(SEMAPHORE_MODIFY_STATE, FALSE, ("Global\\" + target_name + "_rcvr_sem").c_str());

DWORD intr_process_locking::wait(unsigned long period)

   return WaitForSingleObject(sndr_sem, period);


void intr_process_locking::signal()

   ReleaseSemaphore(trgt_sem, 1, 0);

接收线程函数:

void intr_process_comm::rcvr_thread_proc()

  while (conn_state == intr_process_comm::opened) 
    try 
      // wait on rcvr_semaphore for an infinite time
      WaitForSingleObject(rcvr_sem, INFINITE);
      if (inner_release) // if the semaphore was released within this process
        return;
      // once signaled by another process, get the message
      std::string msg_str((LPCSTR)hmf_mapview);
      // signal one of the waiters that want to put messages 
      // in this process's memory area
      // 
      // this doesn't change ANYTHING in execution, commented or not..
      //ReleaseSemaphore(wait_sem, 1, 0);

      // put this message in this process's queue
      Msg msg = Msg::from_xml(msg_str);
      if (msg.command == "connection")
        process_connection_message(msg);
  
      in_messages.enQ(msg);
      //std::cout << "\n  Message: \n"<< msg << "\n";
    
    catch (std::exception e) 
      std::cout << "\n  Ran into trouble getting the message. Details: " << e.what();
    
  

发送线程函数:

void intr_process_comm::sndr_thread_proc()

  while (conn_state == intr_process_comm::opened ||
    (conn_state == intr_process_comm::closing && out_messages.size() > 0)
    ) 
    // pull a message out of the queue
    Msg msg = out_messages.deQ();

    if (connections.find(msg.destination) == connections.end())
      connections[msg.destination].connect(msg.destination);

    if (connections[msg.destination].connect(msg.destination)
      != intr_process_locking::state::opened) 
      blocked_messages[msg.destination].push_back(msg);
      continue;
    

    // THIS ALWAYS GETS GETS WAIT_OBJECT_0 RESULT
    DWORD wait_result = connections[msg.destination].wait(wait_timeout);
    if (wait_result == WAIT_TIMEOUT)   // <---- THIS IS NEVER TRUE
      out_messages.enQ(msg);
      continue;
    
    // do things here
    // release the receiver semaphore in the other process
    connections[msg.destination].signal();
  

澄清一些事情:

发送者中的trgt_sem 是接收者中的rcvr_sem

发送者中的`sndr_sem'是接收者中的'wait_sem'。

【问题讨论】:

一般问题 - 为什么你在这里使用完全信号量而不是事件? 请提供minimal reproducible example 显示您实际在做什么。不要描述代码,展示它。 @RbMm 我有一个包含单个消费者和多个生产者的缓冲区。 @RemyLebeau 好了,代码。不能少放多一点就太多了。 OpenSemaphore(SEMAPHORE_MODIFY_STATE, 已经出错。你不能等待这个对象。对该对象的任何等待函数调用都必须以错误访问被拒绝而失败。你需要使用SYNCHRONIZE|SEMAPHORE_MODIFY_STATE 【参考方案1】:

用一些句柄打电话给WaitForSingleObject

句柄必须具有 SYNCHRONIZE 访问权限。

但您只能使用 SEMAPHORE_MODIFY_STATE 访问权限打开信号量。使用此访问可能调用ReleaseSemaphore(此句柄必须具有SEMAPHORE_MODIFY_STATE 访问权限)但调用WaitForSingleObject 失败,结果为WAIT_FAILED。在此之后调用GetLastError() 必须返回ERROR_ACCESS_DENIED

所以如果我们想同时调用ReleaseSemaphore 和任何等待函数 - 我们需要在句柄上有 SEMAPHORE_MODIFY_STATE | SYNCHRONIZE 访问权限。所以需要用代码打开

OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, )

当然,经常检查 api 返回值和错误代码可以节省大量时间

【讨论】:

【参考方案2】:

如果您将超时设置为 0,WaitForSingleObject 将始终立即返回,成功的 WaitForSingleObject 将返回 WAIT_OBJECT_0(其值恰好为 0),WFSO 不像大多数 API 那样通过非零返回来指示成功。

【讨论】:

is 我说它总是成功的意思。我知道如果等待期为 0,它将返回。但我的问题是,尽管信号量没有被释放,但它总是返回 WAIT_OBJECT_0

以上是关于Windows 命名信号量未锁定的主要内容,如果未能解决你的问题,请参考以下文章

如何正确销毁 C 中多个进程使用的共享未命名信号量?

使用 posix 未命名信号量的 IPC [关闭]

正确销毁命名的 System V 信号量

C++中的多线程,只检查信号量是不是被锁定

如何使用信号量锁定表?

POSIX 信号量