【问题标题】:Using Semaphors to create a thread safe stack in C?使用 Semaphors 在 C 中创建线程安全堆栈?
【发布时间】:2020-08-20 23:27:20
【问题描述】:

我正在尝试创建一个使用信号量实现线程安全的堆栈。当我将单个对象推入堆栈时它可以工作,但是一旦我尝试将第二个项目推入堆栈或从堆栈中弹出一个项目,终端就会冻结。这就是我到目前为止所拥有的,并且不确定我在哪里搞砸了。一切正常,但终端只是如前所述冻结

这里是我创建堆栈的地方

sem_t selements, sspace;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

BlockingStack *new_BlockingStack(int max_size)
{
    sem_init(&selements, 0, 0);
    sem_init(&sspace, 0, max_size);

    BlockingStack *newBlockingStack = malloc(sizeof(BlockingStack));
    newBlockingStack->maxSize = max_size;
    newBlockingStack->stackTop = -1;
    newBlockingStack->element = malloc(max_size * sizeof(void *));

    if (newBlockingStack == NULL)
    {
        return NULL;
    }

    if (newBlockingStack->element == NULL)
    {
        free(newBlockingStack);
        return NULL;
    }

    return newBlockingStack;
}

这里是 Push 和 Pop:

bool BlockingStack_push(BlockingStack *this, void *element)
{
    sem_wait(&sspace);
    pthread_mutex_lock(&m);


    if (this->stackTop == this->maxSize - 1)
    {
        return false;
    }
    if (element == NULL)
    {
        return false;
    }
    this->element[++this->stackTop] = element;
    return true;

    pthread_mutex_unlock(&m);
    sem_post(&selements);

}

void *BlockingStack_pop(BlockingStack *this)
{
    sem_wait(&selements);
    pthread_mutex_lock(&m);

    if (this->stackTop == -1)
    {
        return NULL;
    }
    else
    {
        return this->element[this->stackTop--];
    }

    pthread_mutex_unlock(&m);
    sem_post(&sspace);
}

【问题讨论】:

    标签: c multithreading stack mutex semaphore


    【解决方案1】:

    建议的更改:

    sem_t sem;
    ...
    BlockingStack *new_BlockingStack(int max_size)
    {
        sem_init(&sem, 0, 1);
        ...
    
    bool BlockingStack_push(BlockingStack *this, void *element)
    {
        sem_wait(&sem);
        ...
        sem_post(&sem);
        ...
    

    具体来说:

    • 我只会初始化一个信号量对象,除非我确定我需要其他人

    • 我会为 push() 和 pop() 使用 same 信号量

    • pshared: 0 应该足以同步单个进程中的不同 pthread。

    • 将信号量初始化为1,因为“push”或“pop”的第一件事就是sem_wait()

    【讨论】:

      【解决方案2】:

      为了线程安全,您已经使用了互斥锁 (pthread_mutex_lock(&m) and pthread_mutex_unlock(&m))。为此目的,使用这种互斥就足够了。一旦一个线程获得互斥锁,其他线程就会阻塞pthread_mutex_lock(&m) 调用。 并且只有当前获取互斥锁的线程才能调用pthread_mutex_unlock(&m)

      【讨论】:

      • 我打算只使用互斥锁,但被告知对于这个任务,我必须使用信号量
      【解决方案3】:

      好的,我正在研究这个问题,并在做了一些互联网研究和调试我的代码后终于破解了答案。错误是 mutex_unlock 和 sem_post 必须在返回之前。

      以我的流行音乐为例:

      void *BlockingStack_pop(BlockingStack *this)
      {
          sem_wait(&selements);
          pthread_mutex_lock(&m);
      
          if (this->stackTop == -1)
          {
              return NULL;
          }
          else
          {
              return this->element[this->stackTop--];
          }
      
          pthread_mutex_unlock(&m);
          sem_post(&sspace);
      }
      

      注意 pthread_mutex_unlock(&m);sem_post(&sspace); 是如何出现在返回之后的,它们实际上必须像这样放置在每个返回之前:

      void *BlockingStack_pop(BlockingStack *this)
      {
      ...
          pthread_mutex_unlock(&m);
          sem_post(&sspace);
              return NULL;
      ...
              pthread_mutex_unlock(&m);
          sem_post(&sspace);
              return this->element[this->stackTop--];
      ...
      
      }
      

      【讨论】:

      • 问:那为什么不去掉重复的冗余“pthread_mutex”呢?只使用一个或另一个锁定?摆脱那些不必要的“空间”和“元素”:你只需要一个信号量。
      • @FoggyDay 我使用 sspace 等到堆栈中至少有一个空间,然后再推入堆栈,然后 selements 发出信号,表明在推入后至少有一个元素。使用信号量后我使用互斥锁锁定和解锁
      • IMO... 这太傻了:(你只需要 一个 锁:要么堆栈正在更新(你正在积极地做一个“推”或“弹出” ) ... 或者不是。
      猜你喜欢
      • 2010-10-21
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多