同时写入和读取数组

时间:2016-06-29 19:02:27

标签: c arrays embedded

我目前正在处理项目,其中我使用中断来填充128个值的数组。每个中断都会顺序更新数组中的一个值。当数组已满时,会有一个短暂的延迟,然后该过程将以一批新值开始,以填充数组。问题在于我需要读取这个数组,当我在更新数组时,我不想读它。 我试图在下面的伪代码中实现这一点,但仍然存在指针可能在读取中途交换的问题。 有没有人有任何想法来实现这个目标?

int arrayA[128];
int arrayB[128];

int *readPointer;
int *writePointer;

readPointer = arrayA;
writePointer = arrayB;

void while(1) {
   read();
   delay(100);
}

_interrupt rx() {
   static int index = 0;
   *(writePointer+index) = readVal();
   index++;
   if(index==128) {
      index = 0;
      temp = writePointer;
      writePointer = readPointer;
      readPointer  = temp;
   }
}

void read() {
     int myVal = anyValueInArray;
   }
}

2 个答案:

答案 0 :(得分:1)

您可以使用3个阵列。一个正在写的。一个准备好阅读。一个实际上正在阅读。当阅读器使用它的数组时,它会将其数组交换为准备读取的数组。当编写器填充数组时,它会将其数组交换为准备读取的数组。

int arrayA[128];
int arrayB[128];
int arrayC[128];

int *writePointer = arrayA;
int *readPointer = arrayB;
int *readReady = arrayC;

/* producer */
void on_interrupt () {
    int is_full = fill(writePointer);
    if (is_full) {
        swap(writePointer, readReady);
        ready_to_read_set(true);
        activate_reader();
    }
}

/* consumer */
void reader () {
    swap(readPointer, readReady);
    ready_to_read_set(false);
    drain(readPointer);
}

这假设如果尚未读取已准备好读取的填充数组,则使用新填充的数组将其交换出来没有问题。

您的示例并未显示读者如何等待输入,但无论采用何种机制,都可以使用此方法进行调整,以便作者在可以读取的数组中进行交换。

答案 1 :(得分:1)

根据您需要访问数据的方式和数据速率,我可以建议五种模式(至少)。

  • 在读取操作期间禁用中断 - 简单强力。对现有代码的最小更改。如果读取访问时间太长,则风险会丢失。
  • 自旋锁定数据一致性 - 从当前读取缓冲区读取数据,然后检查读取缓冲区是否已切换 - 如果有,则再次读取,直到读取缓冲区开始与最后的情况相同。
  • 自旋锁定同步 - 轮询在缓冲区交换机上设置的标志,并在时间而不是异步延迟时读取数据。
  • 如果数据也按读取顺序使用环形缓冲区
  • 在缓冲区准备就绪时,在中断时给出一个抢占式调度内核和阻塞信号量。这类似于自旋锁同步选项,但是您可以在等待数据的同时在其他线程中执行有用的工作。实际上,调度程序(通常是RTOS)的可用性为其他解决方案(如消息队列)提供了机会,但在这种情况下可能是一个有点重量级的解决方案。

您还可以使用两倍大小的单个缓冲区来简化中断,并简单地将读/写指针分配给该缓冲区的开头和中间。通过利用长度为2的幂,可以进一步优化中断。

示例自旋锁数据一致性:

#include <stdint.h>
#include <stdbool.h>

#define BUFFER_LEN (1u<<8) // Ensure a power of two 
#define BUFFER_MODULO_MASK (BUFFER_LEN - 1u)
volatile int buffer_array[256];
volatile int* buffer[2] = { &buffer_array[0], &buffer_array[BUFFER_LEN / 2] ;

typedef uint8_t atomic_int_t ;  // Use a type for which read/write is uninterruptable on your target
volatile atomic_int_t read_buffer_select = 1 ;

int main()
{
    for(;;)
    {
        bool data_read = false ;
        atomic_int_t current_buffer_select = read_buffer_select ;
        do
        {
            // Read operation attempt
            readfrom( buffer[current_buffer_select] ) ; 

            if( current_buffer_select == read_buffer_select )
            {
                // Read buffer consistent during read - comeplete
                data_read = true ;
            }
            else
            {
                // Read bufer changed during read - try again
                current_buffer_select = read_buffer_select   
            }

        } while( !data_read ) ;

        delay(100);
    }
}

_interrupt rx() 
{
    static int index = 0;
    buffer_array[index] = readVal();
    index++ ;
    index &= BUFFER_MODULO_MASK ;
    if( index == 0 || index == BUFFER_LEN / 2 )
    { 
        read_buffer_select == 0 ? 1 : 0 ;
    }
}

旋转锁同步示例

如上所述,除了主循环:

    for(;;)
    {
        // Synchronise to buffer completion
        atomic_int_t current_buffer_select = read_buffer_select ;
        while( current_buffer_select == read_buffer_select )
        {
            // do nothing
        }

        // Optimum time to read data
        readfrom( buffer[read_buffer_select] ) ; 
    }

这里没有异步延迟,循环等待直到中断切换缓冲区为您提供读取和处理数据的最大时间。

同步方法具有较少的数据丢失可能性 - 如果数据丢失是一个问题,数据一致性方法依赖于延迟长度足够小以避免数据丢失但足够长以避免两次读取相同数据。除非数据丢失不是问题,否则应首选同步方法。

你当然可以将两者结合起来;同步读取然后检查读取/处理没有花费比缓冲更长的时间。这可能是不必要的 - 应该选择缓冲区长度以避免这种情况。

其他两种方法在实现中可能更复杂(但更灵活),并且在这种情况下它们是否相关并不清楚,所以我没有提供示例。