【问题标题】:Concurrent Queue, C并发队列,C
【发布时间】:2011-05-12 07:17:56
【问题描述】:

所以,我试图在 C 中实现一个并发队列。我将方法分为“读取方法”和“写入方法”。因此,当访问写入方法时,例如 push() 和 pop(),我获得了一个写入器锁。读取方法也是如此。此外,我们可以有多个读者,但只有一个作者。

为了让它在代码中工作,我为整个队列设置了一个互斥锁。还有两个条件锁——一个给作者,另一个给读者。我还有两个整数来跟踪当前使用队列的读取器和写入器的数量。

所以我的主要问题是 - 如何实现多个读取器同时访问读取方法?

目前这是我的一般读取方法代码:(在伪代码中 - 不是 C。我实际上使用的是 pthreads)。

mutex.lock();
while (nwriter > 0) {
  wait(&reader);
  mutex.unlock();
}
nreader++;
//Critical code
nreader--;
if (nreader == 0) {
  signal(&writer)
}
mutex.unlock

所以,假设我们有一个持有互斥锁的阅读器。现在任何其他出现并试图获得互斥锁的读者都无法获得。不会堵吗?那么有多少个reader同时访问read方法呢?

我的推理正确吗?如果是,如何解决?

【问题讨论】:

  • 您需要在混合中添加一个信号量。并查看google.co.uk/…
  • 我相信只使用条件变量是可能的。但是如果我必须使用信号量,你会说我应该用它做什么?
  • 链接是一篇描述使用互斥体和信号量的算法的经典文章。
  • 哦,谢谢!我没有注意到链接。

标签: c concurrency


【解决方案1】:

如果这不是为了练习,请使用来自 pthread 的读写锁(pthread_rwlock_* 函数)。

另请注意,使用锁 stil 保护单个调用可能无法提供必要的正确性保证。例如,从 STL 队列中弹出元素的典型代码是

if( !queue.empty() ) {
    data = queue.top();
    queue.pop();
}

即使在队列方法中使用了锁,这也会在并发代码中失败,因为从概念上讲,这段代码必须是原子事务,但实现不提供这样的保证。线程可能会弹出与 top() 读取的元素不同的元素,或者尝试从空队列中弹出,等等。

【讨论】:

    【解决方案2】:

    请找到以下读\写函数。

    在我的函数中,我使用 canRead 和 canWrite 互斥体和 nReads 来表示读者数量:

    写函数:

    lock(canWrite) // Wait if mutex if not free
    // Write
    unlock(canWrite)
    

    读取函数:

    lock(canRead) // This mutex protect the nReaders
    nReaders++    // Init value should be 0 (no readers)
    if (nReaders == 1) // No other readers
    {
       lock(canWrite)  // No writers can enter critical section
    }
    unlock(canRead)
    
    // Read
    
    lock(canRead)
    nReaders--;
    if (nReaders == 0) // No more readers
    {
       unlock(canWrite) // Writer can enter critical secion
    }
    unlock(canRead)
    

    【讨论】:

    • 此代码假定锁可以从不同的线程获取和释放(canWrite 锁正在被读者使用),因此如果锁实现不允许这样做,则可能无法工作。
    【解决方案3】:

    一个经典的解决方案是多阅读器,单一作者。

    一个数据结构一开始就没有读者也没有写者。

    您允许任意数量的并发读者。

    当一个作家出现时,你会阻止他,直到所有当前的读者都完成;然后你让他走(任何新出现的读者和作者,而作者被阻止在他身后排队,按顺序)。

    【讨论】:

      【解决方案4】:

      你可以试试这个c原生库,无锁,适合跨平台lfqueue,

      例如:-

      int* int_data;
      lfqueue_t my_queue;
      
      if (lfqueue_init(&my_queue) == -1)
          return -1;
      
      /** Wrap This scope in other threads **/
      int_data = (int*) malloc(sizeof(int));
      assert(int_data != NULL);
      *int_data = i++;
      /*Enqueue*/
       while (lfqueue_enq(&my_queue, int_data) == -1) {
          printf("ENQ Full ?\n");
      }
      
      /** Wrap This scope in other threads **/
      /*Dequeue*/
      while  ( (int_data = lfqueue_deq(&my_queue)) == NULL) {
          printf("DEQ EMPTY ..\n");
      }
      
      // printf("%d\n", *(int*) int_data );
      free(int_data);
      /** End **/
      
      lfqueue_destroy(&my_queue);
      

      【讨论】:

        猜你喜欢
        • 2020-07-21
        • 2011-04-14
        • 2017-06-27
        • 2021-01-31
        • 1970-01-01
        • 2013-07-25
        • 2014-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多