AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77224637
Accepted
frankenapps
frankenapps
Asked: 2023-10-04 02:15:33 +0800 CST2023-10-04 02:15:33 +0800 CST 2023-10-04 02:15:33 +0800 CST

Windows 中的互斥体到底什么时候被释放?

  • 772

CreateMutex我成功地使用了一个互斥体,该互斥体是使用bInitialOwnerset to创建的TRUE,以确保只有一个应用程序实例多次运行而不会出现任何问题。

我的期望是,这也将扩展到允许最多两个同时运行的实例。这是我的代码,旨在实现这一目标:

#include <windows.h>
#include <synchapi.h>

#include <codecvt>
#include <chrono>
#include <iostream>
#include <thread>

int main() {
    std::cout << "New instance started.\n";

    HANDLE mutex_handle = CreateMutexW(0, TRUE, L"myMutex1");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        std::cout << "At least one instance is already running.\nChecking if this is the second instance...\n";

        HANDLE mutex_handle2 = CreateMutexW(0, TRUE, L"myMutex2");

        if (GetLastError() == ERROR_ALREADY_EXISTS) {
            std::cout << "A second instance also exists already.\n";
        }
    }

    // In every case wait for 25 seconds to simulate a long running program (e.g. GUI, etc.).
    std::this_thread::sleep_for(std::chrono::milliseconds(25000));

    return 0;
}

三次打开程序后,我在第三次实例上得到了所需的输出:

New instance started.
At least one instance is already running.
Checking if this is the second instance...
A second instance also exists already.

然而,即使在关闭除一个打开的实例之外的所有实例之后(为了简单起见,假设除了第三个实例之外的所有实例都已关闭),我仍然得到上面的输出,而不是预期的输出:

New instance started.

但是,关闭所有实例后,程序将再次按预期运行。这让我相信,互斥锁只有在所有实例关闭后才会被释放,但是文档指出:

使用CloseHandle函数关闭句柄。当进程终止时系统自动关闭句柄。当互斥对象的最后一个句柄关闭时,该对象将被销毁。

因此,我希望互斥体在其所属进程终止后就会被释放。

为什么情况似乎并非如此?我如何使用互斥体将我的应用程序限制为两个实例?

附加信息

我的怀疑是,每个后续线程也通过调用获取了一个句柄CreateMutexW,因此互斥体仅在最后一个句柄关闭时才被销毁(例如,“批处理”的最后一个运行实例被关闭并且没有更多实例正在运行)。

CloseHandle为了缓解这种情况,我认为只要遇到线程,我就可以使用获得的线程句柄进行调用ERROR_ALREADY_EXISTS。实现此功能的代码如下,但这会导致与上述完全相同的行为:

#include <windows.h>
#include <synchapi.h>

#include <codecvt>
#include <chrono>
#include <iostream>
#include <thread>

int main() {
    std::cout << "New instance started.\n";

    HANDLE mutex_handle = CreateMutexW(0, TRUE, L"myMutex1");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex_handle != NULL)
        {
            CloseHandle(mutex_handle);
        }

        std::cout << "At least one instance is already running.\nChecking if this is the second instance...\n";

        HANDLE mutex_handle2 = CreateMutexW(0, TRUE, L"myMutex2");
        if (GetLastError() == ERROR_ALREADY_EXISTS) {
            if (mutex_handle2 != NULL)
            {
                CloseHandle(mutex_handle2);
            }
            
            std::cout << "A second instance also exists already.\n";
        }
    }

    // In every case wait for 25 seconds to simulate a long running program (e.g. GUI, etc.).
    std::this_thread::sleep_for(std::chrono::milliseconds(25000));

    return 0;
}

编辑:上面的代码实际上确实按照我的预期方式运行。我在测试它时犯了一个错误(通过使用初始代码运行旧的二进制文件)。

c++
  • 2 2 个回答
  • 101 Views

2 个回答

  • Voted
  1. Best Answer
    Ted Lyngmo
    2023-10-04T03:43:50+08:002023-10-04T03:43:50+08:00

    您在拥有状态下创建互斥体,但不会调用ReleaseMutex让其他进程进入。由于您仅使用互斥体来计算实例而不执行任何其他同步,因此只需将它们创建为“无主”就足够了 - 如果您尝试创建的互斥锁已存在,请关闭它并尝试下一步,直到达到最大实例数。

    它可能看起来像这样:

    template<std::size_t I>
    class InstanceCounter {
    public:
        InstanceCounter() {
            for(m_id = 0; m_id < I; ++m_id) {
                hMtx = CreateMutexW(nullptr, false,
                                    (L"MUTEX" + std::to_wstring(m_id)).c_str());
                if(not hMtx)
                    throw std::runtime_error("Could not create mutex");
                if(GetLastError() != ERROR_ALREADY_EXISTS)
                    return;        // we found a free slot
                CloseHandle(hMtx); // don't lock this one up
            }
            // max instances reached
            throw std::runtime_error("Max instances running");
        }
        InstanceCounter(const InstanceCounter&) = delete;
        InstanceCounter& operator=(const InstanceCounter&) = delete;
        ~InstanceCounter() { CloseHandle(hMtx); }
    
        std::size_t my_id() const { return m_id; }
    
    private:
        HANDLE hMtx;
        std::size_t m_id;
    };
    

    你可以像这样使用它。以下是最多允许 3 个实例的示例:

    int main() {
        try {
            InstanceCounter<3> ic;
    
            std::cout << ic.my_id() << '\n';
    
            std::this_thread::sleep_for(std::chrono::seconds(10));
    
            std::cout << ic.my_id() << " Done\n";
        } catch(const std::exception& ex) {
            std::cerr << "Exception: " << ex.what() << '\n';
        }
    }
    
    • 3
  2. Blindy
    2023-10-04T02:32:34+08:002023-10-04T02:32:34+08:00

    互斥锁仅处于活动或非活动状态。如果您希望传递非二进制数量的等待调用,则可以使用专门为此设计的原语,例如semaphores,它可以定义要传递的最大调用数量(在您的情况下为 2)。

    这种方法的唯一问题是,与互斥体不同,您还需要处理应用程序关闭(以减少信号量计数,以便另一个实例可以“进入”)。一般来说,如果您同时需要 2 个实例,则没有理由不允许任意数量的实例。

    • 1

相关问题

  • 为什么编译器在这里错过矢量化?

  • 使用带有库的 CMake 编译错误[关闭]

  • 每次我尝试运行预制时都会抛出错误

  • 如何在 C++ 中创建类似于 std::byte 的八位字节类型?

  • C++17 中 std::byte 只能按位运算?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve