• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

C++并发和互斥保护展示

武飞扬头像
技术的微光
帮助1

        最近要写一个多线程的并发数据库,主要是希望使用读写锁实现库的并发访问,同时考虑到其他平台(如Iar)没有C 的读写锁,需要操作系统提供,就将读写锁封装起来。整个过程还是比较曲折的,碰到了不少问题,在此就简单分析总结下并发和互斥吧。

        首先,先贴上一部分源代码:

  1.  
    #include <shared_mutex>
  2.  
    #include <iostream>
  3.  
    #include <windows.h>
  4.  
    #include <synchapi.h>
  5.  
     
  6.  
    using cegn_mutex = std::shared_mutex;
  7.  
    cegn_mutex g_cegn_mutex;
  8.  
    void cegn_mutex_unique_lck(cegn_mutex& testmutex) //独占锁,写数据
  9.  
    {
  10.  
    std::unique_lock<cegn_mutex> cegn_lock(testmutex);
  11.  
    }
  12.  
     
  13.  
    void cegn_mutex_share_lck(cegn_mutex& Dbmutex) //共享锁,读数据
  14.  
    {
  15.  
    std::shared_lock<cegn_mutex> cegn_lock(Dbmutex);
  16.  
    }
  17.  
     
  18.  
    void cegn_mutex_unlck(cegn_mutex& Dbmutex)
  19.  
    {
  20.  
    ; //vc读写锁离开作用域自动释放
  21.  
    }
  22.  
     
  23.  
    int g_dwVal = 0;
  24.  
    void FastWriteData(int i)
  25.  
    {
  26.  
    while (1)
  27.  
    {
  28.  
    cegn_mutex_unique_lck(g_cegn_mutex);
  29.  
    g_dwVal ;
  30.  
    std::cout << "FastWriteData " << " Set dwVal= " << g_dwVal << "\n";
  31.  
     
  32.  
    Sleep(1000);
  33.  
    cegn_mutex_unlck(g_cegn_mutex);
  34.  
    }
  35.  
    }
  36.  
     
  37.  
    void SlowWriteData(int i)
  38.  
    {
  39.  
    while (1)
  40.  
    {
  41.  
    cegn_mutex_unique_lck(g_cegn_mutex);
  42.  
    g_dwVal ;
  43.  
    std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
  44.  
    Sleep(5000);
  45.  
    cegn_mutex_unlck(g_cegn_mutex);
  46.  
    }
  47.  
    }
  48.  
     
  49.  
    void ReadData(int i)
  50.  
    {
  51.  
    while (1)
  52.  
    {
  53.  
    cegn_mutex_share_lck(g_cegn_mutex);
  54.  
    std::cout << "ReadData " << " Get dwVal= " << g_dwVal << "\n";
  55.  
    Sleep(500);
  56.  
    cegn_mutex_unlck(g_cegn_mutex);
  57.  
    }
  58.  
    }
  59.  
     
  60.  
    int main()
  61.  
    {
  62.  
    std::cout << "main start !!" << std::endl;
  63.  
     
  64.  
    std::thread thread1 = std::thread(FastWriteData, 0);
  65.  
    std::thread thread2 = std::thread(SlowWriteData, 0);
  66.  
    thread1.join();
  67.  
    thread2.join();
  68.  
     
  69.  
    getchar();
  70.  
    return 1;
  71.  
    }
学新通

代码不长,逻辑也挺清晰的,但结果不正确:

学新通

似乎就没有互斥保护,因为FastWriteData和SlowWriteData中都独占了cegn_mutex_unique_lck(g_cegn_mutex);

且在while(1)中,不存在释放写锁的情况,那就不应该两个写线程交替出现。

如上让chatgpt分析下,它认为没啥问题,我尝试修改回标准读写锁接口,如下:

  1.  
    void FastWriteData(int i)
  2.  
    {
  3.  
    while (1)
  4.  
    {
  5.  
    // cegn_mutex_unique_lck(g_cegn_mutex);
  6.  
    std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
  7.  
    g_dwVal ;
  8.  
    std::cout << "FastWriteData " << " Set dwVal= " << g_dwVal << "\n";
  9.  
     
  10.  
    Sleep(1000);
  11.  
    cegn_mutex_unlck(g_cegn_mutex);
  12.  
    }
  13.  
    }
  14.  
     
  15.  
    void SlowWriteData(int i)
  16.  
    {
  17.  
    while (1)
  18.  
    {
  19.  
    // cegn_mutex_unique_lck(g_cegn_mutex);
  20.  
    std::unique_lock<cegn_mutex> lck(g_cegn_mutex);
  21.  
    g_dwVal ;
  22.  
    std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
  23.  
    Sleep(5000);
  24.  
    cegn_mutex_unlck(g_cegn_mutex);
  25.  
    }
  26.  
    }
学新通

 如上,代码运行就是正常了

  1.  
    main start !!
  2.  
    FastWriteData Set dwVal= 1
  3.  
    FastWriteData Set dwVal= 2
  4.  
    FastWriteData Set dwVal= 3
  5.  
    FastWriteData Set dwVal= 4
  6.  
    FastWriteData Set dwVal= 5
  7.  
    FastWriteData Set dwVal= 6
  8.  
    FastWriteData Set dwVal= 7
  9.  
    FastWriteData Set dwVal= 8
  10.  
    FastWriteData Set dwVal= 9
  11.  
    FastWriteData Set dwVal= 10
  12.  
    FastWriteData Set dwVal= 11
  13.  
    FastWriteData Set dwVal= 12
  14.  
    FastWriteData Set dwVal= 13
  15.  
    FastWriteData Set dwVal= 14
学新通

现在FastWriteData就独占了互斥量,导致SlowWriteData无法运行。为啥使用接口:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)    //独占锁,写数据
{
    std::unique_lock<cegn_mutex> cegn_lock(testmutex);
}

就不行了?

修改成直接调用:

  1.  
    using cegn_mutex = std::shared_mutex;
  2.  
    cegn_mutex g_cegn_mutex;
  3.  
    void cegn_mutex_unique_lck(cegn_mutex& testmutex) //独占锁,写数据
  4.  
    {
  5.  
    // std::unique_lock<cegn_mutex> cegn_lock(testmutex);
  6.  
    std::unique_lock<cegn_mutex> cegn_lock(g_cegn_mutex);
  7.  
    }

还是不能正确互斥,修改如下也一样:

  1.  
    void cegn_mutex_unique_lck(cegn_mutex& testmutex) //独占锁,写数据
  2.  
    {
  3.  
    // std::unique_lock<cegn_mutex> cegn_lock(testmutex);
  4.  
    std::unique_lock<std::shared_mutex> cegn_lock(g_cegn_mutex);
  5.  
    }

经过分析,问题是:

void cegn_mutex_unique_lck(cegn_mutex& testmutex)

函数中定义了一个互斥量cegn_lock :

std::unique_lock<cegn_mutex> cegn_lock(testmutex);

该互斥量在函数退出的时候,生命周期就结束了,所以自动销毁,最终导致无法互斥,那是在想要封装,如何实现呢,可以自己协议个类封装:

完整的简单代码如下:

  1.  
    #include <iostream>
  2.  
    #include <thread>
  3.  
    #include <mutex>
  4.  
    #include <windows.h>
  5.  
     
  6.  
    class MutexWrapper {
  7.  
    public:
  8.  
    MutexWrapper(std::mutex& mutex) : m_mutex(mutex) {
  9.  
    m_mutex.lock();
  10.  
    }
  11.  
     
  12.  
    ~MutexWrapper() {
  13.  
    m_mutex.unlock();
  14.  
    }
  15.  
     
  16.  
    private:
  17.  
    std::mutex& m_mutex;
  18.  
    };
  19.  
     
  20.  
    std::mutex g_mutex_test;
  21.  
    int g_dwVal = 0;
  22.  
     
  23.  
    void FastWriteData(int i) {
  24.  
    while (1) {
  25.  
    MutexWrapper lock(g_mutex_test);
  26.  
    g_dwVal ;
  27.  
    std::cout << "FastWriteData " << " Set dwVal= " << g_dwVal << "\n";
  28.  
    Sleep(1000);
  29.  
    }
  30.  
    }
  31.  
     
  32.  
    void SlowWriteData(int i) {
  33.  
    while (1) {
  34.  
    MutexWrapper lock(g_mutex_test);
  35.  
    g_dwVal ;
  36.  
    std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
  37.  
    Sleep(3000);
  38.  
    }
  39.  
    }
  40.  
     
  41.  
    int main() {
  42.  
    std::cout << "main start !!" << std::endl;
  43.  
     
  44.  
    std::thread thread1 = std::thread(FastWriteData, 0);
  45.  
    std::thread thread2 = std::thread(SlowWriteData, 0);
  46.  
    thread1.join();
  47.  
    thread2.join();
  48.  
     
  49.  
    getchar();
  50.  
    return 1;
  51.  
    }
学新通

如此,运行正常了

学新通

修改下例程,让两个进程都整行跑 

  1.  
    void FastWriteData(int i) {
  2.  
    while (1) {
  3.  
    {
  4.  
    MutexWrapper lock(g_mutex_test);
  5.  
    g_dwVal ;
  6.  
    std::cout << "FastWriteData " << " Set dwVal= " << g_dwVal << "\n";
  7.  
    }
  8.  
    Sleep(1000);
  9.  
    }
  10.  
    }
  11.  
     
  12.  
    void SlowWriteData(int i) {
  13.  
    while (1) {
  14.  
    {
  15.  
    MutexWrapper lock(g_mutex_test);
  16.  
    g_dwVal ;
  17.  
    std::cout << "SlowWriteData " << " Set dwVal= " << g_dwVal << "\n";
  18.  
    }
  19.  
    Sleep(3000);
  20.  
    }
  21.  
    }
学新通

如上,代码就基本都正常了。

当然,也可以将互斥锁修改为读写锁,如下:

  1.  
    class MutexWrapper {
  2.  
    public:
  3.  
    MutexWrapper(std::shared_mutex& mutex) : m_mutex(mutex) {
  4.  
    m_mutex.lock();
  5.  
    }
  6.  
     
  7.  
    ~MutexWrapper() {
  8.  
    m_mutex.unlock();
  9.  
    }
  10.  
     
  11.  
    private:
  12.  
    std::shared_mutex& m_mutex;
  13.  
    };
  14.  
     
  15.  
    std::shared_mutex g_mutex_test;
学新通

代码也运行正常了。

综上:

1:基于RAII,C 的很多变量生命周期有限,必须特别注意智能变量的生命周期。

2:如果需要封装读写锁,不能简单函数分装,实在不行,就用一个类封装吧

3:要熟练掌握std::thread,std::shared_mutex,std::mutex的用法,这个是变法互斥基本要求

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhieefgh
系列文章
更多 icon
同类精品
更多 icon
继续加载