C++:如何简化线程锁?

Posted

技术标签:

【中文标题】C++:如何简化线程锁?【英文标题】:C++: How To Simplified Thread Lock? 【发布时间】:2014-04-18 12:57:04 【问题描述】:

我是 C++ 新手。在学习线程如何工作时,我发现在开头调用 WaitForSingleObject(x) 并在结尾调用 ReleaseMutex(x) 非常烦人。所以我写了一个类来为我做这件事,但我不确定影响,我做对了吗。想知道有没有更简化的方法来实现同样的目标?这是我的做法:

class MutexLock 
public:
    MutexLock(HANDLE hMutex) 
        m_hMutex = hMutex;
    
    void    Lock() 
        WaitForSingleObject((m_hMutex), INFINITE);
    

~MutexLock() 
    if (m_hMutex != NULL) 
        ReleaseMutex(m_hMutex);
        std::cout << "Mutex released." << std::endl;
    


private:
    HANDLE  m_hMutex;
;

以及我如何使用课程:

class TestMutex

public:
    TestMutex(void) 
        m_mutex = CreateMutex(NULL, FALSE, NULL);
        std::cout << "Mutex created." << std::endl;
    
    ~TestMutex(void) 
        if (m_mutex != NULL)
            CloseHandle(m_mutex);
    

    void    Func1(void) 
        MutexLock ml(m_mutex);
        ml.Lock();

        std::cout << "Func1: Owning mutex." << std::endl;
        std::cout << "Press enter key to end this." << std::endl;
        ReadKey(GetStdHandle(STD_INPUT_HANDLE));
    
    void    Func2(void) 
        MutexLock ml(m_mutex);
        ml.Lock();
        //std::cout << "Press enter key to start this." << std::endl;
        //ReadKey(GetStdHandle(STD_INPUT_HANDLE));

        std::cout << "Func2: Owning mutex." << std::endl;
        std::cout << "Press enter key to end this." << std::endl;
        ReadKey(GetStdHandle(STD_INPUT_HANDLE));
    

private:
    HANDLE  m_mutex;
;

在主函数中:

int _tmain(int argc, _TCHAR* argv[])

    TestMutex * tm = new TestMutex();

    HANDLE aThread[2];
    for (int i = 0; i < 2; i++)
    
        aThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)tm, 0, 0);
    

    WaitForMultipleObjects(2, aThread, TRUE, INFINITE);

    for (int i = 0; i < 2; i++)
    
        CloseHandle(aThread[i]);
    

    delete tm;
    ReadKey(GetStdHandle(STD_INPUT_HANDLE));

    return 0;

其他人通常是这样做线程锁的吗?

【问题讨论】:

为什么不使用Thread support library 中的标准习语?这应该会大大简化您的代码! 使用std::lock_guardstd::thread,如果需要,使用std::mutex 【参考方案1】:

实际上,您允许您的类 API 的使用者滥用它,而您可以很容易地防止这种情况发生。 他必须在施工后准确地调用一次锁。他可能不知道这个,忘记调用它,或者调用两次。

更简单且不易出错的方法是将锁定方法设为私有并从构造函数中调用它。

但是,正如其他评论者所写,最好的办法是使用现有的库。 除了其他人提到的,Qt 还有一个不错的 QMutexLocker 类。

【讨论】:

现在我明白这有什么问题了。谢谢你的好建议。【参考方案2】:

我不相信它会比你所拥有的更“简单”。您需要相互排除正在被众多进程修改的共享变量。您可能会遇到不需要互斥锁的“只读”类型的情况,但这无助于您的简化。

在销毁时释放互斥锁(当它超出范围时)可能是最好的方法,我已经看到许多线程库使用完全相同的方法。 Portable Tools Library,PTLib,是一个包含线程抽象的抽象库,当互斥锁超出范围但您仍然必须使用它们时,它会释放互斥锁。但是,您还应该跟踪对互斥锁的调用和释放次数,以便您可以在其他线程可用时向它发出信号。

此外,正如 Bgie 在他的回答中指出的那样,您确实需要保护您的代码。 永远不要相信其他程序员,包括你未来的自己。

但是你在离开作用域时释放锁的想法是一个很好的第一个通用实现,只是需要一些额外的工作:)。

(根据 Bgie 的评论进行编辑)

【讨论】:

您没有注意到代码中的错误。该类不跟踪锁定的调用次数,并且总是释放一次。 好吧,有经验的人总是会注意到一些问题,但菜鸟不会。当我编写这个测试程序时,我想这个场景会是这样的:TestMutex 类具有所有线程都可以访问的共享资源,但是当任何线程正在修改它时,在修改之前没有其他线程应该能够获得这个共享资源完成。 当我说 noob 看不到问题时,我其实是想问为什么我们需要跟踪调用次数和发布次数? @tongko 你要问自己,等待线程在做什么?它是否不断查询互斥锁(在循环中,不建议这样做,因为该线程正在使用 CPU 周期,而它所做的一切都是无缘无故地循环)?它是否等待信号继续?程序员怎么会知道这个互斥锁只能使用一次?

以上是关于C++:如何简化线程锁?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中正确地在循环中使用互斥锁?

C++ 无锁队列与多线程崩溃

C++笔记--Linux编程(14)-线程同步

C++多线程1.2-线程安全的保证——互斥量mutex(锁)和原子变量atomic

C++多线程1.2-线程安全的保证——互斥量mutex(锁)和原子变量atomic

一组线程的 C++ 互斥锁