【问题标题】:How to implement the pseudocode in the book of semaphores?如何实现信号量书中的伪代码?
【发布时间】:2022-01-05 20:48:04
【问题描述】:

最近我学习了一门操作系统课程,该课程将我从信号量的小书中发送到屏障伪代码。但是现在几个小时我一直在努力实现这个障碍,我似乎无法正确理解它。为了理解它,我尝试了一个简单的程序,让线程进入障碍,当所有线程都到达时,让它们通过。 这是我的代码:

#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>


#define NR_MAX 5

int n=NR_MAX;
int entered = 0;

pthread_mutex_t mtx;
sem_t smph;

void* bariera(void *v){
    pthread_mutex_lock(&mtx);
        entered++ ;
    printf("thread %d have entered\n", entered);
    pthread_mutex_unlock(&mtx);
    if(entered == n) { 
        
        sem_post(&smph); printf("Out %d \n", entered);}
    sem_wait(&smph);
    
    sem_post(&smph);
}

int main() {
    pthread_t thr[NR_MAX];
    pthread_mutex_init(&mtx, NULL);
    sem_init(&smph, 0, 1);
    for (int i=0; i<NR_MAX; i  ){
        pthread_create(&thr[i], NULL, bariera, NULL);
    }
    for(int i=0; i<NR_MAX; i  ){
        pthread_join(thr[i], NULL);
        }
    return 0;
}

这应该如何实际实施?因为现在,它只打印他们到达障碍物的顺序,然后只打印最后一个到达的顺序。

编辑:完全忘记了,这是伪代码:

n = the number of threads
count = 0  - keeps track of how many threads arrived at the barrier
mutex = Semaphore (1)  - provides exclusive acces to count
barrier = Semaphore (0) - barrier is locked (zero or negative) until all threads arrive; then it should be unlocked(1 or more)


rendezvous
2
3 mutex.wait()
4 count = count + 1
5 mutex.signal ()
6
7 if count == n: barrier.signal ()
8
9 barrier.wait()
10 barrier.signal ()
11
12 critical point

预期输出:

   Out 5
   Out 4 
   Out 3 
   Out 2
   Out 1

(顺序不必相同)

实际输出:

Out 5

【问题讨论】:

  • 信号量小书中的屏障伪代码”。不能指望每个人都读过或拥有这本书的副本。请在问题本身中给出伪代码。还要给出确切的预期输出与实际输出。
  • 伪代码看起来有竞争条件。 count = count + 1 是安全的 [mutex protected] 但它与 if count == n [refetches count mutex锁定]竞争。你为什么不[只是]使用pthread_barrier_init/pthread_barrier_wait?或者,如果您尝试重新发明/重新实现它们,请查看它们的源代码以获取干净/调试的 impl。
  • 我有一个任务,为了更好地理解信号量,我必须实现这个类似屏障的伪代码。另外,我不认为比赛条件现在是一个问题,因为我想要的只是打印一些东西,正如我所说,顺序并不重要
  • 也许你的程序应该在线程通过障碍后做一些事情,这样你就可以看到它是否在工作?
  • 不,比赛会阻止它正常工作。只需:temp = ++count; 然后if (n == temp)。如果您想了解信号量/障碍,那么您应该想要了解竞争条件。

标签: c pthreads semaphore barrier


【解决方案1】:

三个问题:

  • 信号量初始化不正确。
  • 在临界区之外访问entered
  • 错位printf
  • 缺少return
  • for 循环中缺少增量。
void* bariera(void *v) {
    int id = (int)(uintptr_t)v;
    printf("[%d] Before barrier.\n", id);

    pthread_mutex_lock(&mtx);
    if(++entered == n) 
        sem_post(&smph);  // Wake up a thread.
    pthread_mutex_unlock(&mtx);

    sem_wait(&smph);  // Barrier.
    sem_post(&smph);  // Wake up another thread.

    // Do something after the barrier.
    printf("[%d] After barrier.\n", id);

    return NULL;
}

sem_init(&smph, 0, 0);  // Should initially be zero.
for (int i=0; i<NR_MAX; ++i) {
    pthread_create(&thr[i], NULL, bariera, (void*)(intptr_t)i);
}

输出:

[0] Before barrier.
[2] Before barrier.
[3] Before barrier.
[4] Before barrier.
[1] Before barrier.
[1] After barrier.
[0] After barrier.
[3] After barrier.
[2] After barrier.
[4] After barrier.

这使得屏障信号量无法重用。要解决这个问题,因为它会发布 n+1 次。要将其恢复为原始状态,我们只需发布n 次即可。

void* bariera(void *v) {
    int id = (int)(uintptr_t)v;
    printf("[%d] Before barrier.\n", id);

    pthread_mutex_lock(&mtx);
    if(++entered == n)
        for (int i=n; i--; )
           sem_post(&smph);  // Wake up every thread.
    pthread_mutex_unlock(&mtx);
    sem_wait(&smph);  // Barrier.

    // Do something after the barrier.
    printf("[%d] After barrier.\n", id);

    return NULL;
}

【讨论】:

    【解决方案2】:

    使用 C11 原子类型,您实际上不需要单独的互斥锁来保护对屏障计数器的访问,如下所示。该版本还将屏障相关的变量和操作封装成一个结构体和函数,并且不需要最后一个碰到屏障的线程也必须在信号量上等待。

    #include <stdatomic.h>
    #include <stdio.h>
    
    #include <pthread.h>
    #include <semaphore.h>
    
    struct barrier {
      int maxcount;
      _Atomic int count;
      sem_t sem;
    };
    
    void barrier_init(struct barrier *b, int count) {
      b->maxcount = b->count = count;
      sem_init(&b->sem, 0, 0);
    }
    
    void barrier_destroy(struct barrier *b) {
      sem_destroy(&b->sem);
    }
    
    void barrier_wait(struct barrier *b) {
      // Atomically subtract a number and return the *old* value
      if (atomic_fetch_sub_explicit(&b->count, 1, memory_order_acq_rel) == 1) {
        // Wake up all waiting threads as they're all at the barrier now
        for (int n = 0; n < b->maxcount - 1; n += 1) {
          sem_post(&b->sem);
        }
      } else {
        sem_wait(&b->sem); // Actual barrier; wake for wakeup
      }
    }
    
    void* wait_func(void *vb) {
      struct barrier *b = vb;
      printf("Thread 0x%x before barrier.\n", (unsigned)pthread_self());
      barrier_wait(b);
      printf("Thread 0x%x after barrier.\n", (unsigned)pthread_self());
      return NULL;
    }
    
    #define NTHREADS 5
    int main(void) {
      pthread_t threads[NTHREADS];
      struct barrier b;
    
      barrier_init(&b, NTHREADS);
      for (int n = 0; n < NTHREADS; n += 1) {
        pthread_create(&threads[n], NULL, wait_func, &b);
      }
      for (int n = 0; n < NTHREADS; n += 1) {
        pthread_join(threads[n], NULL);
      }
      barrier_destroy(&b);
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-09
      • 1970-01-01
      • 2016-02-24
      • 1970-01-01
      • 2011-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多