为什么我的生产者 - 消费者模式有意外的输出?

时间:2013-05-05 00:48:31

标签: c multithreading operating-system simulation nios

this excercise(生产者 - 消费者)中,3个生产者各自生成一个主题并生成素数。但是当我运行程序时,消耗的第一个素数不是它产生的第一个素数,所以我得不到预期的输出。能帮我找到并纠正错误吗?

这是带有模式的主文件:

#include <stdio.h>
#include "oslab_lowlevel_h.h"

int NextPrime( int );

#define FIFO_SIZE 10

/* Declare a structure to hold a producer's starting value,
 * and an integer for the Producer-number (Producer 1, 2 or 3). */
struct Prod {
    int startvalue;
    int id;
};

unsigned int stack1[0x400]; /* Stack for thread 1 */
unsigned int stack2[0x400]; /* Stack for thread 2 */
unsigned int stack3[0x400]; /* Stack for thread 3 */
unsigned int stack4[0x400]; /* Stack for thread 4 */
unsigned int stack5[0x400]; /* Stack for thread 5 */

/* Declare variables for the First-In-First-Out Queue */
int  Fifo[FIFO_SIZE];        /* Array holding FIFO queue data. */
int  rdaddr;                /* Next unread entry when reading from queue. */
int  wraddr;                /* Next free entry when writing into queue. */

/* Declaration of semaphore variables.
 * 
 * Sorry for the lack of comments, but part of the purpose of the lab
 * is that you should find things out by reading the actual code. */
int  rdmutex = 1;
int  wrmutex = 1;
int  nrempty = FIFO_SIZE;
int  nrfull = 0;

/*
 * fatal_error
 * 
 * Print a message, then stop execution.
 * This function never returns; after printing
 * the message, it enters an infinite loop.
 */
void fatal_error( char * msg)
{
  printf( "\nFatal error: %s\n", msg );
  while( 1 );
}

/*
 * Sleep
 * 
 * Delay execution by keeping the CPU busy for a while,
 * counting down to zero.
 */
void Sleep (int n)
{
    while (n--);
}

/*
 * Signal
 * 
 * Semaphore operation: add to semaphore,
 * possibly allowing other threads to continue.
 */
void Signal( int *sem )
{
  /* We must disable interrupts, since the operation
   * *sem = *sem + 1
   * will require several machine instructions on Nios2.
   * If we have a timer-interrupt and a thread-switch
   * somewhere in the middle of those machine instructions,
   * the semaphore will be updated twice, or not at all, or
   * in some other erroneous way.
   */
  oslab_begin_critical_region();
  *sem = *sem + 1;
  oslab_end_critical_region();
}

/*
 * Wait
 * 
 * Sempahore operation: check semaphore, and
 * wait if the semaphore value is zero or less.
 */
void Wait( int *sem )
{
  /* Disable interrupts. */
  oslab_begin_critical_region();
  while ( *sem <= 0 )
    {
      /* If we should wait, enable interrupts again. */
      oslab_end_critical_region();

      //  oslab_yield(); /* Perhaps we should yield here? */

      /* Disable interrupts again before next iteration in loop. */
      oslab_begin_critical_region();
    }
    /* We have waited long enough - the semaphore-value is now
     * greater than zero. Decrease it. */
    *sem = *sem - 1;
    /* Enable interrupts again. */
    oslab_end_critical_region();
}

/*
 * PutFifo
 * 
 * Insert an integer into the FIFO queue.
 */
void PutFifo( int tal )
{
  //  Wait (&nrempty);      /* Wait for nrempty? */
  //  Wait (&wrmutex);      /* Wait for wrmutex? */

  Fifo[wraddr] = tal;       /* Write to FIFO array. */
    //  printf("\nPutFifo:  %d ", tal); /* Optional debug output */
    //  printf("\nwraddr = %d ", wraddr); /* Optional debug output. */
  wraddr = wraddr + 1;      /* Increase index into FIFO array,
                               to point to the next free position. */
  /* Wrap around the index, if it has reached the end of the array. */
  if (wraddr == FIFO_SIZE ) wraddr = 0;

  //  Signal (&wrmutex);    /* Signal wrmutex? */
  //  Signal (&nrfull);     /* Signal nrfull? */
}

/*
 * GetFifo
 * 
 * Extract the next integer from the FIFO queue.
 */
int GetFifo( void )
{
  int retval;               /* Declare temporary for return value. */

  //  Wait (&nrfull);       /* Wait for nrfull? */
  //  Wait (&rdmutex);      /* Wait for rdmutex? */

  retval = Fifo[rdaddr];    /* Get value from FIFO array. */
    //  printf("\nGetFifo:  %d ", retval); /* Optional debug output */
    //  printf("\nrdaddr = %d ", rdaddr); /* Optional debug output */
  rdaddr = rdaddr + 1;      /* Increase index into FIFO array,
                               to point to the next free position. */
  /* Wrap around the index, if it has reached the end of the array. */
  if (rdaddr == FIFO_SIZE ) rdaddr = 0;

  //  Signal (&rdmutex);    /* Signal rdmutex? */
  //  Signal (&nrempty);    /* Signal nrempty? */

  return (retval);          /* Return value fetched from FIFO. */
}

/*
 * NextPrime
 * 
 * Return the first prime number larger than the integer
 * given as a parameter. The integer must be positive.
 * 
 * *** NextPrime is outside the focus of this assignment. ***
 * The definition of NextPrime can be found at the end of this file.
 * The short declaration here is required by the compiler.
 */
int NextPrime( int );

void Producer( struct Prod * prodstruct )
{
  int next;                 /* Will hold the prime we just produced. */
  int prodid;               /* Tells whether we are producer 1, 2 or 3. */
  next = prodstruct -> startvalue; /* Get starting value from parameter. */
  prodid = prodstruct -> id;/* Get producer number from parameter. */
  while( 1 )                /* Loop forever. */
  {
    next = NextPrime (next);/* Produce a new prime. */
    printf("\nNext Prime from producer %d is %d",prodid,next); /* Informational output. */
    PutFifo(next);          /* Write prime into FIFO. */
  //  oslab_yield();        /* Perhaps we should yield here? */ 
  }
}

void Consumer( int * tal )
{
  int next;                 /* Will hold the prime we are to consume. */
  int consid = *tal;        /* Tells whether we are consumer 1 or 2. */
  while( 1 )                /* Loop forever. */
  {
    next = GetFifo();       /* Get a newly produced prime from the FIFO. */
    printf("\nConsumer %d gets Prime %d ",consid, next); /* Informational output. */
    Sleep(2000);            /* Symbolic work. */
  //  oslab_yield();        /* Perhaps we should yield here? */ 
  }
}

int main( void )
{
  int new_thread_id; /* Thread ID variable. */
  struct Prod prod1, prod2, prod3;  /* Producer starting-values. */
  int cons1, cons2;                 /* Consumer starting-values. */

  rdaddr = 0;               /* FIFO initialization. */
  wraddr = 0;               /* FIFO initialization. */
  printf("\nSystem starting...");

  prod1.startvalue = 2000;
  prod1.id = 1;

  prod2.startvalue = 5000;
  prod2.id = 2;

  prod3.startvalue = 8000;
  prod3.id = 3;

  cons1 = 1;
  cons2 = 2;

  new_thread_id = oslab_create_thread((void *)Producer, &prod1, &(stack1[0x3ff]));
  if( new_thread_id < 0 ) fatal_error( "cannot start Producer 1" );
  printf("\nProducer %d is created with thread-ID %d", prod1.id, new_thread_id);

  new_thread_id = oslab_create_thread((void *)Producer, &prod2, &(stack2[0x3ff]));
  if( new_thread_id < 0 ) fatal_error( "cannot start Producer 2" );
  printf("\nProducer %d is created with thread-ID %d", prod2.id, new_thread_id);

  new_thread_id = oslab_create_thread((void *)Producer, &prod3, &(stack3[0x3ff]));
  if( new_thread_id < 0 ) fatal_error( "cannot start Producer 3" );
  printf("\nProducer %d is created with thread-ID %d", prod3.id, new_thread_id);

  new_thread_id = oslab_create_thread((void *)Consumer, &cons1, &(stack4[0x3ff]));
  if( new_thread_id < 0 ) fatal_error( "cannot start Consumer 1" );
  printf("\nConsumer %d is created with thread-ID %d", cons1, new_thread_id);

  new_thread_id = oslab_create_thread((void *)Consumer, &cons2, &(stack5[0x3ff]));
  if( new_thread_id < 0 ) fatal_error( "cannot start Consumer 2" );
  printf("\nConsumer %d is created with thread-ID %d", cons2, new_thread_id);

  oslab_idle(); /* Must be called here! */
}


/*
 * NextPrime
 * 
 * Return the first prime number larger than the integer
 * given as a parameter. The integer must be positive.
 */
#define PRIME_FALSE   0     /* Constant to help readability. */
#define PRIME_TRUE    1     /* Constant to help readability. */
int NextPrime( int inval )
{
   int perhapsprime;        /* Holds a tentative prime while we check it. */
   int testfactor;          /* Holds various factors for which we test perhapsprime. */
   int found;               /* Flag, false until we find a prime. */

   if (inval < 3 )          /* Initial sanity check of parameter. */
   {
     if(inval <= 0) return(1);  /* Return 1 for zero or negative input. */
     if(inval == 1) return(2);  /* Easy special case. */
     if(inval == 2) return(3);  /* Easy special case. */
   }
   else
   {
     /* Testing an even number for primeness is pointless, since
      * all even numbers are divisible by 2. Therefore, we make sure
      * that perhapsprime is larger than the parameter, and odd. */
     perhapsprime = ( inval + 1 ) | 1 ;
   }
   /* While prime not found, loop. */
   for( found = PRIME_FALSE; found != PRIME_TRUE; perhapsprime += 2 )
   {
     /* Check factors from 3 up to perhapsprime/2. */
     for( testfactor = 3; testfactor <= (perhapsprime >> 1) + 1; testfactor += 1 )
     {
       found = PRIME_TRUE;      /* Assume we will find a prime. */
       if( (perhapsprime % testfactor) == 0 ) /* If testfactor divides perhapsprime... */
       {
         found = PRIME_FALSE;   /* ...then, perhapsprime was non-prime. */
         goto check_next_prime; /* Break the inner loop, go test a new perhapsprime. */
       }
     }
     check_next_prime:;         /* This label is used to break the inner loop. */
     if( found == PRIME_TRUE )  /* If the loop ended normally, we found a prime. */
     {
       return( perhapsprime );  /* Return the prime we found. */
     } 
   }
   return( perhapsprime );      /* When the loop ends, perhapsprime is a real prime. */
} 

其余文件可用here

当我运行代码时,我得到了生产者的预期输出,但我没有得到消费者的预期输出:

System starting...
Producer 1 is created with thread-ID 1
Producer 2 is created with thread-ID 2
Producer 3 is created with thread-ID 3
Consumer 1 is created with thread-ID 4
Consumer 2 is created with thread-ID 5
#### Thread yielded after using 1 tick.
Performing thread-switch number 1. The system has been running for 1 ticks.
Switching from thread-ID 0 to thread-ID 1.

Next Prime from producer 1 is 2003
PutFifo:  2003 
wraddr = 0 
Next Prime from producer 1 is 2011
PutFifo:  2011 
wraddr = 1 
Next Prime from producer 1 is 2017
PutFifo:  2017 
wraddr = 2 
Next Prime from producer 1 is 2027
PutFifo:  2027 
wraddr = 3 
Next Prime from producer 1 is 2029
PutFifo:  2029 
wraddr = 4 
Next Prime from producer 1 is 2039
PutFifo:  2039 
wraddr = 5 
Next Prime from producer 1 is 2053
PutFifo:  2053 
wraddr = 6 
Next Prime from producer 1 is 2063
PutFifo:  2063 
wraddr = 7 
Next Prime from producer 1 is 2069
PutFifo:  2069 
wraddr = 8 
Next Prime from producer 1 is 2081
PutFifo:  2081 
wraddr = 9 
Next Prime from producer 1 is 2083
PutFifo:  2083 
wraddr = 0 
Next Prime from producer 1 is 2087
PutFifo:  2087 
wraddr = 1 
Next Prime from producer 1 is 2089
PutFifo:  2089 
wraddr = 2 
Next Prime from producer 1 is 2099
PutFifo:  2099 
wraddr = 3 
Next Prime from producer 1 is 2111
PutFifo:  2111 
wraddr = 4 
Next Prime from producer 1 is 2113
PutFifo:  2113 
wraddr = 5 
Next Prime from producer 1 is 2129
PutFifo:  2129 
wraddr = 6 
Next Prime from producer 1 is 2131
PutFifo:  2131 
wraddr = 7 
Next Prime from producer 1 is 2137
PutFifo:  2137 
wraddr = 8 
Next Prime from producer 1 is 2141
PutFifo:  2141 
wraddr = 9 
Next Prime from producer 1 is 2143
PutFifo:  2143 
wraddr = 0 
Next Prime from producer 1 is 2153
PutFifo:  2153 
wraddr = 1 
Performing thread-switch number 2. The system has been running for 101 ticks.
Switching from thread-ID 1 to thread-ID 2.

Next Prime from producer 2 is 5003
PutFifo:  5003 
wraddr = 2 
Next Prime from producer 2 is 5009
PutFifo:  5009 
wraddr = 3 
Next Prime from producer 2 is 5011
PutFifo:  5011 
wraddr = 4 
Next Prime from producer 2 is 5021
PutFifo:  5021 
wraddr = 5 
Next Prime from producer 2 is 5023
PutFifo:  5023 
wraddr = 6 
Next Prime from producer 2 is 5039
PutFifo:  5039 
wraddr = 7 
Next Prime from producer 2 is 5051
PutFifo:  5051 
wraddr = 8 
Next Prime from producer 2 is 5059
PutFifo:  5059 
wraddr = 9 
Next Prime from producer 2 is 5077
PutFifo:  5077 
wraddr = 0 
Next Prime from producer 2 is 5081
PutFifo:  5081 
wraddr = 1 
Performing thread-switch number 3. The system has been running for 201 ticks.
Switching from thread-ID 2 to thread-ID 3.

Next Prime from producer 3 is 8009
PutFifo:  8009 
wraddr = 2 
Next Prime from producer 3 is 8011
PutFifo:  8011 
wraddr = 3 
Next Prime from producer 3 is 8017
PutFifo:  8017 
wraddr = 4 
Next Prime from producer 3 is 8039
PutFifo:  8039 
wraddr = 5 
Next Prime from producer 3 is 8053
PutFifo:  8053 
wraddr = 6 
Next Prime from producer 3 is 8059
PutFifo:  8059 
wraddr = 7 
Performing thread-switch number 4. The system has been running for 301 ticks.
Switching from thread-ID 3 to thread-ID 4.

GetFifo:  5077 
rdaddr = 0 
Consumer 1 gets Prime 5077 
GetFifo:  5081 
rdaddr = 1 
Consumer 1 gets Prime 5081 
GetFifo:  8009 
rdaddr = 2 
Consumer 1 gets Prime 8009 
GetFifo:  8011 
rdaddr = 3 
Consumer 1 gets Prime 8011 
GetFifo:  8017 
rdaddr = 4 
Consumer 1 gets Prime 8017 
GetFifo:  8039 
rdaddr = 5 
Consumer 1 gets Prime 8039 
GetFifo:  8053 
rdaddr = 6 
Consumer 1 gets Prime 8053 
GetFifo:  8059 
rdaddr = 7 
Consumer 1 gets Prime 8059 
GetFifo:  5051 
rdaddr = 8 
Consumer 1 gets Prime 5051 
GetFifo:  5059 
rdaddr = 9 
Consumer 1 gets Prime 5059 
GetFifo:  5077 
rdaddr = 0 
Consumer 1 gets Prime 5077 
GetFifo:  5081 

你能不能告诉我为什么前30个素数被覆盖的时候我只是遵循规范,只是删除代码中的注释来激活教师为我们学习准备的内容?因为我没有得到任何好的帮助,我已经几个月都无法完成这项练习。前30个素数被严重覆盖,程序不应该改变(这是作业)。我问导师,他没有,说我正在使用更新版本的软件。我可以尝试旧版本的软件,但这似乎不是一个可能的解决方案。

更新

我能想到的策略是开始使用调试器并在执行期间检查FIFO ADT。我没有太多使用gdb的经验,所以请尽可能帮助我。

3 个答案:

答案 0 :(得分:3)

原帖2013-05-05

在评论中,我注意到:

  

你有包装控件,这样当编写器到达数组的末尾时,下一个条目放在开头,但你没有任何过度填充控件,所以如果生产者生产的速度比消费者消耗的快,生产者覆盖未读数据。您需要确保数组Fifo没有过度填充。

Nick Rosencrantz观察到:

  

你是对的,将FIFO大小增加到100就可以修复bug。

实际上,增加FIFO大小修复错误;它只是避免了更长时间的错误。您需要跟踪写指针(索引)是否要赶上读指针,并且不要添加或延迟添加新数字,直到其中一个消费者已读取读指针处的数字,以便有空间再一次。


代码的实际测试

问题中的代码是逐字练习的代码。我不确定所描述的设备是什么,但屏幕截图显示Windows涉及。我有一台Mac。从表面上看,这使得测试代码很难 - 其中一个文件是汇编程序。但是,代码可以很容易地通过原语的Unix(POSIX pthread)实现来补充。实际上,实验室设计得很好;这很容易做模拟。但是,你必须使用适当的盐来报告我报告的任何结果 - Mac是一台非常不同的机器。

  1. 我更喜欢在定义或使用之前声明的函数。

    #include <unistd.h>
    extern void fatal_error(char * msg);
    extern void Sleep (int n); 
    extern void Signal(int *sem);
    extern void Wait(int *sem);
    extern void PutFifo(int tal);
    extern int  GetFifo(void);
    extern void Producer(struct Prod * prodstruct);
    extern void Consumer(int * tal);
    
  2. while (1);中的fatal_error()循环是忙碌的等待;我宁愿使用pause()。然而,它从未实际行使过,所以也许没关系。

  3. 可以使用POSIX pthreads轻松模拟必要的oslab_*()原语:

    /* pthread implementation */
    #include <pthread.h>
    #include <time.h>
    
    static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
    
    void oslab_begin_critical_region(void)
    {
        pthread_mutex_lock(&mtx);
    }
    void oslab_end_critical_region(void)
    {
        pthread_mutex_unlock(&mtx);
    }
    int oslab_create_thread(int (*thread_function)(void *), void *data, unsigned int *stack)
    {
        typedef void *(*ThreadMain)(void *);
        static int threadnum = 0;
        pthread_t pth;
        pthread_attr_t pat;
        pthread_attr_init(&pat);
        pthread_attr_setdetachstate(&pat, PTHREAD_CREATE_DETACHED);
        if (pthread_create(&pth, &pat, (ThreadMain)thread_function, data) != 0)
        {
            char buffer[128];
            sprintf(buffer, "Failed to create thread with stack %p\n", stack);
            fatal_error(buffer);
        }
        return ++threadnum;
    }
    void oslab_idle(void)
    {
        pause();
    }
    void oslab_yield(void)
    {
        struct timespec rqtp = { .tv_sec = 0, .tv_nsec = 1000000 };  // 1 millisecond
        if (nanosleep(&rqtp, 0) != 0)
            fatal_error("nanosleep failed\n");
    }
    /* end pthread implementation */
    
  4. 所有邮件都处于恼人的格式"\nMessage";这是一种螺旋形式。重写所有内容,以便换行符结尾,“行尾”字符始终应该是。

  5. 启用调试打印时,应强行退出行。在调试print语句之后,我使用fflush(0)(在此上下文中等效于fflush(stdout))。另一种(更好)的方法是在main()中调用setvbuf()来设置行缓冲。

    char buffer[BUFSIZ];
    setvbuf(stdout, buffer, _IOLBF, BUFSIZ);
    
  6. 结果

    随着这些变化(保留同步原语 - 调用Wait()Signal()oslab_yield() - 已注释掉),所有地狱都会崩溃:

    System starting...
    Producer 1 is created with thread-ID 1
    Producer 2 is created with thread-ID 2
    Producer 3 is created with thread-ID 3
    Consumer 1 is created with thread-ID 4
    Consumer 2 is created with thread-ID 5
    Consumer 1 gets Prime 0
    Next Prime from producer 1 is 2003
    Consumer 2 gets Prime 0
    Next Prime from producer 2 is 5003
    Next Prime from producer 3 is 8009
    Consumer 1 gets Prime 0
    Next Prime from producer 1 is 2011
    Consumer 2 gets Prime 0
    Next Prime from producer 2 is 5009
    Consumer 1 gets Prime 0
    Next Prime from producer 1 is 2017
    Consumer 2 gets Prime 0
    Next Prime from producer 3 is 8011
    Next Prime from producer 2 is 5011
    Consumer 1 gets Prime 0
    Next Prime from producer 1 is 2027
    Consumer 2 gets Prime 0
    Consumer 1 gets Prime 0
    Next Prime from producer 3 is 8017
    Next Prime from producer 2 is 5021
    Next Prime from producer 1 is 2029
    Consumer 2 gets Prime 0
    Consumer 1 gets Prime 2003
    Consumer 2 gets Prime 2029
    Next Prime from producer 1 is 2039
    Next Prime from producer 3 is 8039
    Next Prime from producer 2 is 5023
    Consumer 1 gets Prime 8009
    Consumer 2 gets Prime 2011
    

    但是,如果启用同步原语(但禁用调试代码),那么您将获得理智的行为:消费者在队列中有数据之前不会尝试读取队列,并且编写者不会尝试当队列中没有空格时写入队列。

    System starting...
    Producer 1 is created with thread-ID 1
    Producer 2 is created with thread-ID 2
    Producer 3 is created with thread-ID 3
    Consumer 1 is created with thread-ID 4
    Consumer 2 is created with thread-ID 5
    Next Prime from producer 1 is 2003
    Next Prime from producer 2 is 5003
    Next Prime from producer 3 is 8009
    Consumer 1 gets Prime 2003
    Consumer 2 gets Prime 5003
    Next Prime from producer 1 is 2011
    Next Prime from producer 3 is 8011
    Next Prime from producer 2 is 5009
    Next Prime from producer 2 is 5011
    Consumer 1 gets Prime 8009
    Consumer 2 gets Prime 2011
    Next Prime from producer 1 is 2017
    Next Prime from producer 3 is 8017
    Consumer 1 gets Prime 8011
    Consumer 2 gets Prime 5009
    Next Prime from producer 2 is 5021
    Next Prime from producer 1 is 2027
    Next Prime from producer 3 is 8039
    Next Prime from producer 2 is 5023
    Consumer 1 gets Prime 5011
    Consumer 2 gets Prime 2017
    Next Prime from producer 3 is 8053
    Next Prime from producer 1 is 2029
    Next Prime from producer 2 is 5039
    

    这是可以预料的。如果你有真正的多核线程(英特尔酷睿i7),那么没有同步,你会得到各种奇怪的行为。通过同步,一切都很平静。我让代码运行自由,输出转到文件。当分析结果时,你会看到每个素数2003..4999的一个外观,每个素数5003..7993的两次出现,以及从8009向上的每个素数的三次出现,这就是你要做的期望的。

    如果启用调试代码,则可以看到更多输出:

    System starting...
    Producer 1 is created with thread-ID 1
    Next Prime from producer 1 is 2003
    Producer 2 is created with thread-ID 2
    PutFifo:  2003
    wraddr = 0
    Producer 3 is created with thread-ID 3
    Consumer 1 is created with thread-ID 4
    GetFifo:  2003
    rdaddr = 0
    Consumer 2 is created with thread-ID 5
    Next Prime from producer 2 is 5003
    Next Prime from producer 3 is 8009
    Consumer 1 gets Prime 2003
    PutFifo:  5003
    wraddr = 1
    GetFifo:  5003
    rdaddr = 1
    Consumer 1 gets Prime 5003
    Next Prime from producer 1 is 2011
    PutFifo:  2011
    wraddr = 2
    GetFifo:  2011
    rdaddr = 2
    Consumer 2 gets Prime 2011
    PutFifo:  8009
    wraddr = 3
    Next Prime from producer 2 is 5009
    PutFifo:  5009
    wraddr = 4
    GetFifo:  8009
    rdaddr = 3
    Consumer 1 gets Prime 8009
    Next Prime from producer 3 is 8011
    GetFifo:  5009
    PutFifo:  8011
    rdaddr = 4
    Next Prime from producer 1 is 2017
    wraddr = 5
    Consumer 2 gets Prime 5009
    Next Prime from producer 2 is 5011
    PutFifo:  5011
    wraddr = 6
    PutFifo:  2017
    wraddr = 7
    GetFifo:  8011
    rdaddr = 5
    Consumer 2 gets Prime 8011
    GetFifo:  5011
    rdaddr = 6
    Next Prime from producer 3 is 8017
    Consumer 1 gets Prime 5011
    PutFifo:  8017
    wraddr = 8
    Next Prime from producer 2 is 5021
    PutFifo:  5021
    wraddr = 9
    Next Prime from producer 1 is 2027
    PutFifo:  2027
    wraddr = 0
    GetFifo:  2017
    rdaddr = 7
    Consumer 1 gets Prime 2017
    

    这显示wraddr从9到0包裹。这是另外的冗长和令人兴奋的。

    运行GDB

    目前尚不清楚您是否能够在原始环境中对该程序运行GDB。您正在使用自制线程(如果您使用的是原始oslab_lowlevel_c.coslab_lowlevel_asm.s源),GDB将不会注意多线程。

    使用我使用的POSIX线程,可以使用GDB调试代码。

答案 1 :(得分:1)

最简单的方法是使用一个不能生成有效值的“特殊值”,并让生产者只将数据放入具有该特殊值的槽中,如果没有,则会睡眠。消费者将使用任何非特殊值的值,并将该位置设置为特殊值。如果没有非特殊值的数据,消费者将会睡觉。

答案 2 :(得分:1)

如果您不取消注释 PutFifo 中的等待信号调用,则无法正常运行此代码 GetFifo 功能。如果它确实在特定情况下在某台计算机上工作,那就是纯粹的运气。

首先,如果一个或多个生产者线程在切换到其中一个消费者线程之前填满FIFO缓冲区,那么其中一些数据显然会丢失。

您可以在示例输出中清楚地看到这一点。生产者线程在第一个消费者线程有机会运行之前生成了38个值。并且因为缓冲区大小为10,消费者将要读取的第一个值实际上是产生的第31个值(即写入 wraddr 0的最后一个值)。

╔═══════╦════════╦═══════╗
║ count ║ wraddr ║ value ║
╠═══════╬════════╬═══════╣
║    .. ║     .. ║    .. ║
║    29 ║      8 ║  5051 ║
║    30 ║      9 ║  5059 ║
║    31 ║      0 ║  5077 ║ <- Consumer starts here
║    32 ║      1 ║  5081 ║
║    33 ║      2 ║  8009 ║
║    34 ║      3 ║  8011 ║
║    35 ║      4 ║  8017 ║
║    36 ║      5 ║  8039 ║
║    37 ║      6 ║  8053 ║
║    38 ║      7 ║  8059 ║
╚═══════╩════════╩═══════╝

另外,如果消费者线程在切换回其中一个生产者线程之前读取的数据多于FIFO缓冲区中可用的数据,那么它们最终会多次读取相同的值。您可以再次从示例输出中看到这一点。消费者线程读取项目31到38,然后回绕到项目29和30( wraddr 8和9的最后一个值),然后再次重复项目31。

这不是可能发生的最糟糕的事情。在真正的抢占式多线程系统中,生产者线程可以在 PutFifo 函数的中途被抢占。因此,当 wraddr 为9时,想象一个生产者线程正在写入FIFO缓冲区。假设它执行这两行。

Fifo[wraddr] = tal;       /* Write to FIFO array. */
wraddr = wraddr + 1;      /* Increase index into FIFO array,

此时 wraddr 为10,但在函数有机会检查溢出(并将索引包回0)之前,该线程被另一个生产者线程抢占。由于 wraddr 为10,这个新的生产者将要写入缓冲区的末尾,可能导致应用程序崩溃。

如果它存活, wraddr 将再次递增(变为11),但它仍然不会回零,因为溢出检查期望与 FIFO_SIZE <完全匹配/ em>的。因此,即使它没有立即崩溃,它肯定会在某些时候崩溃,因为 wraddr 将继续变得越来越大,覆盖越来越多的内存。

最重要的是,如果您希望此代码正常工作,则必须添加同步调用。