我可以在 std::shared_mutex 上使用 std::shared_lock 更改数据吗?

Posted

技术标签:

【中文标题】我可以在 std::shared_mutex 上使用 std::shared_lock 更改数据吗?【英文标题】:Can I change data with a std::shared_lock on a std::shared_mutex? 【发布时间】:2020-05-12 04:12:35 【问题描述】:

我有多个缓冲区与多个读取器/写入器线程共享,并且不同的写入器以不同的方式更改数据。

例如,Writer1 只是追加新数据,而 Writer2 扩展缓冲区大小(重新分配内存和移动数据)。

如果我用一个互斥锁来同步所有对数据的访问,性能可能不会更好,因为大多数读取器只需要读取一个缓冲区,而大多数写入器只需将一小段数据写入单个缓冲区缓冲区。

如果我为每个缓冲区准备一个互斥体,线程之间的锁定/解锁关系会更加复杂。

现在我想确认一件事:

如果写入者仅使用互斥锁上的 shared_lock 更改数据,其他人是否会看到具有相同互斥锁上的 unique_lock/shared_lock 的脏数据?

我编写了一个实验程序如下,看起来没有错误,但我还是不敢在产品中使用。

atomic_bool    g_abShouldRun = true;
sem_t          g_semDoIt1;
sem_t          g_semDone1;
sem_t          g_semDoIt2;
sem_t          g_semDone2;
shared_mutex   g_mutex;
int g_iX = 3, g_iY = 9, g_iR1 = 1, g_iR2 = 3;

void writer() 
   std::srand( 8 );

   while( g_abShouldRun ) 
      sem_wait( &g_semDoIt1 );
      while( rand() % 8 != 0 )
         ;

      
         shared_lock<shared_mutex> lk( g_mutex );
         g_iX *= 2;
         g_iY *= 2;
      
      sem_post( &g_semDone1 );
   ;
;

void reader() 
   std::srand( 8 );

   while( g_abShouldRun ) 
      sem_wait( &g_semDoIt2 );
      while( rand() % 8 != 0 )
         ;

      
         unique_lock<shared_mutex> lk( g_mutex );
         g_iR1 = g_iX;
         g_iR2 = g_iY;
      
      sem_post( &g_semDone2 );
   ;
;

int main( int argc, char** argv ) 
   int iLasting = 10, iError = 0;
   if( argc > 1 )
      iLasting = atoi( argv[1] );
   steady_clock::time_point tpEnd = steady_clock::now() + seconds( iLasting );

   if( sem_init( &g_semDoIt1, 0, 0 ) || sem_init( &g_semDone2, 0, 0 ) ||
         sem_init( &g_semDoIt2, 0, 0 ) || sem_init( &g_semDone2, 0, 0 ) ) 
      cerr << "Failed to create semaphors." << endl;
      return EXIT_FAILURE;
   

   thread thd1( writer );
   thread thd2( reader );

   while( steady_clock::now() < tpEnd ) 
      sem_post( &g_semDoIt1 );
      sem_post( &g_semDoIt2 );
      sem_wait( &g_semDone1 );
      sem_wait( &g_semDone2 );
      if( g_iR1 * 3 != g_iR2 )
         ++iError;
   
   g_abShouldRun = false;
   sem_post( &g_semDoIt1 );
   sem_post( &g_semDoIt2 );
   thd1.join();
   thd2.join();
   sem_destroy( &g_semDoIt1 );
   sem_destroy( &g_semDoIt2 );
   sem_destroy( &g_semDone1 );
   sem_destroy( &g_semDone2 );
   cout << "Error:" << iError << endl;
   return EXIT_SUCCESS;
;

【问题讨论】:

【参考方案1】:

快速浏览一下会跳出以下问题:

    的时候把代码改成unique_lock; 在阅读时将代码改为shared_lock读取时不要修改其他通用全局变量——这实际上是在写入,只是在不同的地方; 有多少 shared_mutexs, function using unique_lock, function using shared_lock 元组你将使用多个线程和多个缓冲区,你需要自己弄清楚——但它会介于 1 和缓冲区数之间。

【讨论】:

感谢您的帮助!但是我不打算修复示例代码,我需要确认我的想法。 我真的不知道你的想法是什么。您在写作时使用shared_lock,在阅读时使用unique_lock。这在概念上是错误的,但在这种特殊情况下,当使用 单个编写器 时,它仍然可以工作。当使用多个作家时,它肯定会失败。 是的,我知道只能有一位作家同时写作。事实上,在锁定这个 shared_mutex 之前,还有另一个互斥锁将确保只有一个写入器同时通过。因为我的产品代码逻辑太复杂,所以我只展示一个示例程序。

以上是关于我可以在 std::shared_mutex 上使用 std::shared_lock 更改数据吗?的主要内容,如果未能解决你的问题,请参考以下文章

std::shared_mutex 是不是偏爱作者而不是读者?

C++关于锁的总结

boost的线程库和c++11的线程序有何不同

锁前后检查资源

如何使用自动布局在情节提要上使网格并排 uiview 响应

如何在Web上使字体变薄