【问题标题】:Indexing into an array of semaphores索引到信号量数组
【发布时间】:2015-11-02 06:24:34
【问题描述】:

假设我初始化了两个全局信号量数组,

semaphore empty[someNum];

semaphore full[someNum];

someNum被初始化为

const int someNum = 3;(全球)

我将有一个名为 init() 的方法,里面是一个 for-loop 来帮助索引这些数组。

for (index=0; index<someNum; index++) num[index]=0;

我的目标是在数组中使用waitsignal 之类的命令,例如,如果num 已满,那么我不希望我的生产者将值放入其中。

init() 内部我知道我需要初始化循环体中的所有信号量。到目前为止我已经尝试过

empty[index] = create_semaphore(1)

full[index] = create_semaphore(0)

在方法中,我想执行 waitsignal 的操作,例如

wait(empty) ;

但我得到了错误。我知道这是因为我使用的是数组的名称而不是特定的信号量。

所以我的问题是,如何正确索引到我的 for-loop 中的数组以指定一个信号量?

感谢您的所有意见!如果您有任何问题,请随时要求任何澄清!请耐心等待,信号量对我来说是一个新概念。

注意

命令create_semaphore 存在于与此相关的另一个程序中。

编辑

foodPass.cpp

#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include <string>
#include "sem.h"

using namespace std ;

/* ######################################## */
/*             Misc Data Types              */
/* ######################################## */
     /* A data type - a struct with an int field
        to represent a child ID at the program level. */
struct threadIdType
{
  int id ;
};

/* ######################################## */
/*             Global Variables             */
/* ######################################## */
//const int numTrivets = 6 ;
const int numTrivets = 3 ;
const int numDiners = numTrivets - 1 ;
const int maxDishNames = 13 ;
//const int numDishNames = 13 ;
const int numDishNames = 5 ;

int trivet[numTrivets] ;

string dishName[maxDishNames];

      /* Here declare the semaphores and other variables you will
         need to synchronize threads. */

sim_semaphore  empty[numTrivets] ; 
sim_semaphore  full[numTrivets] ; 

      /* child_t are global variables to represent the
         dynamically-created threads. */

pthread_t child_t[numTrivets] ;

/* ######################################## */
/*      "Special"   Global Variables        */
/* ######################################## */

/* Code in sem.cpp "expects" the two variables below to be here.
   This particular program does not use "checking." */

         /* "Checking" is just a flag that you set to 1 if you want lots of
            debugging messages and set to 0 otherwise.  The semaphore code in
            sem.cpp imports "checking".  Therefore the semaphore operations
            will write lots of messages if you set checking=1.  */

int checking ;

      /* In some programs, we use the "stdoutLock" variable declared below to
         get intelligible printouts from multiple concurrent threads that write
         to the standard output.  (There has to be something to prevent the
         output of the threads from interleaving unintelligibly on the standard
         output, and we can't use semaphores if the semaphore code is writing
         messages too.)

         To print a message to standard output, a thread first locks standard
         output, then writes, then unlocks standard output.  See files sem.cpp
         or conc.cpp for examples of code that write messages in this manner.

         WARNING:  DON'T change how the locking of standard output is done
         until you've thought a WHOLE lot about the consequences.  In
         particular, using semaphores to do the job of stdoutLock can cause
         "infinite recursion" under certain circumstances.  The reason is that
         the semaphore code itself imports "stdoutLock" and writes messages
         when the "checking" variable is set to 1. */

pthread_mutex_t stdoutLock ;

/* ################################################## */
/*                         init                       */
/* ################################################## */
void init()
{
  int index ;

  srandom(time((time_t *) 0)); /* INITIALIZE RANDOM NUMBER GENERATOR */

  checking = 0 ;

       /* Initialize the "special lock" that is used only to get
          exclusive access to the screen. */

  if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
  {  cout << "MUTEX INITIALIZATION FAILURE!" << endl;
     exit(-1) ;}

    /* Initialize the trivets to indicate that each contains "no
       dish." */

  for (index=0; index<numTrivets; index++) trivet[index]=0;

    /* Here initialize the semaphores and other variables you use
       for synchronization.  */

  for (index=0; index<numTrivets; index++) full[index] = create_sim_sem(0) ;
  for (index=0; index<numTrivets; index++) empty[index] = create_sim_sem(1) ;


 /* Give some mnemonic names to the dishes.  The first name is
    used for an empty trivet.  The last name denotes the check
    (bill) for the meal.  This is coded so no changes are needed
    here as long as the value of "numDishNames" is between 2 and
    13. */

  dishName[0]="no dish";
  dishName[1]="vegetable soup" ;
  dishName[2]="bread and butter" ;
  dishName[3]="beets and chickpeas" ;
  dishName[4]="hardboiled eggs" ;
  dishName[5]="calf tongue" ;
  dishName[6]="baked potato" ;
  dishName[7]="string beans" ;
  dishName[8]="rack of lamb" ;
  dishName[9]="salad" ;
  dishName[10]="coffee" ;
  dishName[11]="flan" ;
  dishName[numDishNames-1]="check" ;

}

/* ################################################## */
/*                    DelayAsMuchAs                   */
/* ################################################## */
void delayAsMuchAs (int limit)
{
  int time, step;
  time=(int)random()%limit;
  for (step=0;step<time;step++) sched_yield() ;
}

/* ################################################## */
/*                       Server                       */
/* ################################################## */
/*

     The mother thread spawns a child thread that executes this
     function.  This function carries out the job of the server
     at the restaurant.

*/
void * Server(void * ignore)
{
  int i, j, delayLimit=100 ;

  for (i=1; i<numDishNames; i++)
  {

        /* I delay a random time before I "feel like" placing
           another dish on the table.*/

    delayAsMuchAs(delayLimit);

      /* When the trivet is available, I place the dish on the
         trivet to my right. */

       /* Here do a synchronization task.  One thing you need to
          do is be sure that you are not going to place a dish on
          a trivet that alreay has a dish on it.  *DO NOT* just
          busy-wait until you see that the trivet is empty. */

    wait_sem(empty[i]) ; 

    trivet[0]=i; // put dish #i onto trivet #0
    pthread_mutex_lock(&stdoutLock) ;
    cout << "Server places " << dishName[trivet[0]]
         << " on trivet #0." << endl ;
    pthread_mutex_unlock(&stdoutLock);

       /* Here you may want to a synchronization task --
          something that "opens the door" for diner #0 to get
          access to the new dish. */

    signal_sem(full[i]) ; 


  }
  pthread_exit ((void *)0) ;
}

/* ################################################## */
/*                         Diner                      */
/* ################################################## */
/*

     The mother thread spawns child threads that execute this
     function.  This function carries out the job of one of the
     diners at the restaurant.

*/

void * Diner(void * postnPtr)
{
       /* Type cast the parameter to recover "position" -- which
          tells me the position at which I am seated at the
          table. */
int position = ((threadIdType *)(postnPtr))->id ;

  int i, j, delayLimit=100 ;

  for (i=1; i<numDishNames; i++)
  {
        /* I delay a random time before I "feel like" picking up the next
           dish.*/

    delayAsMuchAs(delayLimit);

      /* When available, I pick up the next new dish on my left. */

       /* Here do a synchronization task.  One thing you need to
          do is be sure that there is a new dish on the trivet to
          your left now, and that the person on your left has
          "let go" of it. */

    wait_sem(full[i]); 

      /* I declare what I am doing */
    pthread_mutex_lock(&stdoutLock) ;
    cout << "Diner number "<< position ;
    if (i<numDishNames-1) cout << " enjoys ";
    else if (position<numDiners-1) cout << " examines " ;
         else cout << " examines and pays " ;

    cout << dishName[trivet[position]] << endl ;
    pthread_mutex_unlock(&stdoutLock);

        /* I delay a random time to simulate the time it takes for me to
           serve myself some of what is on the dish -- or look at the
           check. */

    delayAsMuchAs(delayLimit);

        /* When available, I place the dish on the trivet to my right. */

       /* Here do a synchronization task.  One thing you need to
          do is be sure that the trivet on your right does not
          have a dish on it now.*/

    wait_sem (empty[i]); 

    pthread_mutex_lock(&stdoutLock) ;
    cout << "Diner number "<< position << " moves "
         << dishName[trivet[position]] << " from trivet #"
         << position << " to trivet #" << position+1 << endl;
    pthread_mutex_unlock(&stdoutLock);
       /* transfer the dish on my left to trivet on my right */
    trivet[position+1]=trivet[position] ;
      /* mark trivet on my left as empty */
    trivet[position]=0;

       /* Here do a synchronization task. You have transferred a
          dish from your left to your right.  The person on your
          left will need to find out that the trivet on your left
          is now empty.  The person on your right will need to
          find out that the trivet on your right now has a new
          dish on it.  */

    signal_sem(empty[i]); 
    signal_sem(full[i]); 

  }
  delete((threadIdType *)(postnPtr)) ;
  pthread_exit ((void *) 0) ;
}

/* ################################################## */
/*                       Busser                       */
/* ################################################## */
/*

     The mother thread spawns children and then executes this
     function.  This is convenient because this function should
     be the last to exit.  This function carries out the job of
     the busser at the restaurant.

*/
void * Busser (void * ignore)
{
  int i, j, delayLimit=100 ;

  for (i=1; i<numDishNames; i++)
  {
        /* I delay a random time before I "feel like" bussing another
           dish.*/

    delayAsMuchAs(delayLimit);

      /* When another dish is on the trivet to my right I remove it. */

       /* Here do a synchronization task.  One thing you need to
          do is be sure that there is a new dish on the trivet to
          your left now, and that the person on your left has
          "let go" of it. */

    wait_sem (full[i]) ; 

    pthread_mutex_lock(&stdoutLock) ;
    cout << "Busser removes "
         << dishName[trivet[numTrivets-1]] << " from trivet #"
         << numTrivets-1<< "." << endl ;
    pthread_mutex_unlock(&stdoutLock);
    trivet[numTrivets-1]=0; // remove the dish.

       /* Here do a synchronization task. The person on your left
          will need to find out that the trivet on your left is
          now empty.  */

    signal_sem (empty[i]); 
  }
  return ignore ;
}

/* ################################################## */
/*                         Main                       */
/* ################################################## */
int main()
{
    init();

    cout << endl << endl;
    cout << "Welcome to the restaurant!" << endl ;
    cout << numDiners << " will be dining." << endl ;
    cout << "The meal will consist of " << numDishNames-2
         << " dishes." << endl;
    cout << "Bon appetite!" << endl ;
    cout << endl << endl;

    int i;

        /* This is a pointer to a struct that contains an int
          field - it is a convenient data type to use as the
          parameter to the child function.  */
    threadIdType * idPtr ;

    for (i=0; i<numDiners; i++)
    {
          /* This records the current index as this child's ID */
       idPtr = new threadIdType ;
       idPtr->id = i ;

       if (0!=pthread_create(&child_t[i], NULL, Diner, (void *) idPtr))
          {cout << "THREAD CREATION FAILURE!" << endl; exit(-1) ;}

       if (0!=pthread_detach(child_t[i]))
          {cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}
    }

       if (0!=pthread_create(&child_t[numDiners], NULL, Server, (void *) 0))
          {cout << "THREAD CREATION FAILURE!" << endl; exit(-1) ;}

       if (0!=pthread_detach(child_t[numDiners]))
          {cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}

    Busser((void *) 0) ;

    cout << endl << endl;
    cout << "Thank you for coming!" << endl ;
    cout << endl << endl;

    return 0 ;
}

【问题讨论】:

  • 你用的是什么库?如果您想等待特定的信号量,那么您必须为其提供索引。我不确定那是你想要的。 POSIX 信号量基于计数器,因此(如果我理解正确的话)可以使用初始化为正确值的单个信号量来执行此操作。
  • 好的,我已将我的库添加到我的编辑中
  • 嗯,我想你知道直接问题的答案,即你不能将数组作为 sem 传递。如果你确定你有正确的实现,那么它就像迭代它们并单独等待它们一样简单。但这些都是阻塞电话,所以我很犹豫在没有看到你具体做什么的情况下提出更多建议。
  • 如果你要索引一个 sem 数组,那么它就是 wait(full[i]) : i = 你想要的索引。我认为您认为它是错误的感觉是对的,但很难说清楚。它可能是正确的,但这取决于它的使用方式。假设 sem 告诉您“这是一次可以访问资源或关键部分的数量”。如果您只想要一个,则将其初始化为 1。但如果您想要多个,则将其初始化为 n。当 sem decs 为
  • full[index]empty[index] 是各自数组之外的“元素”。正如你的教练所说,使用循环。 (已经向您展示了如何操作 - 查看 trivet 初始化循环。)

标签: c++ arrays indexing semaphore


【解决方案1】:

好的,我发现了我的问题。

虽然我在init() 中正确初始化了我的索引数组,

for (index=0; index<numTrivets; index++) full[index] = create_sim_sem(0) ;
for (index=0; index<numTrivets; index++) empty[index] = create_sim_sem(1) ;

我的问题实际上来自waitsignal 命令。

server()是一个纯生产者,只负责第一个数组索引0所以命令很简单

wait_sem(empty[0])signal_sem(full[0])

diner() 中它有点复杂。它充当生产者和消费者之间的混合体,这意味着我们不能只使用01,所以我们看看trivet[] 如何索引单个信号量并借用它,然后我们得到

wait_sem(full[position])

此外,diner() 需要担心两个位置。所以,我们必须使下一个位置独占,所以我们初始化trivet[position+1],并在我们处理它们右边的waitssignals 时再次借用它。

最后,我们有busser(),他纯粹充当消费者。我们只有在他的位置已满时才向他发出信号以供他食用。由于三脚架的数量是可以互换的,(numTrivets)我们不能简单地放入1,因为下次可能有两个以上的位置。因此,我们只需要输入类似

的命令

wait_sem(full[numTrivets-1]) 然后signal_sem(empty[numTrivets-1])

然后我得到我想要的输出。

感谢所有给我意见的人我希望我的回答对所有像我一样困惑的人有用

【讨论】:

    猜你喜欢
    • 2016-08-03
    • 2020-07-12
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多