具有信号量同步的逻辑环缓冲区

时间:2015-11-24 16:58:24

标签: c++ multithreading buffer semaphore buffer-overflow

所以这是我使用信号量同步的逻辑环缓冲区的实现 - 它是一个赋值,这里的大部分内容都是本书中描述的,关于缓冲区和信号量的实现。

有趣的是,这会引发一些奇怪的错误,尤其是Microsoft C++ exception: std::system_error at memory location 0x...,我无法在网上找到任何关于这是什么原因的错误。我假设它可能是一个全局变量的访问,但程序将成功运行9/10次(假设我告诉VS继续),它不是它的时间只是2的索引99处的整数的最后比较int数组。

这是主要方法和生产者/消费者功能。有一些全局变量声明和初始化,然后创建,启动和等待Pro / Con线程,最后比较结果。

#include <iostream>     // cout, cin, ignore
#include <thread>       // thread, join
#include <random>       // rand

/* the number of 'messages' to pass between processes */
const size_t NUMBER_OF_MESSAGES = 100;

/* integer arrays for checking messages after passing */
int PRODUCED_MESSAGES[NUMBER_OF_MESSAGES];
int CONSUMED_MESSAGES[NUMBER_OF_MESSAGES];

/* the logical ring buffer for thread message passing */
LogicalRingBuffer BUFF; // not initiaslized yet ...

void producer_process() {
    for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {
        PRODUCED_MESSAGES[i] = rand();
        BUFF.insert(PRODUCED_MESSAGES[i]);
    }
}

void consumer_process() {
    for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {
        CONSUMED_MESSAGES[i] = BUFF.remove();
    }
}

int main(int agrc, char* argv[]) {
    BUFF = LogicalRingBuffer(); /* initializes the buffer */

    /* creating the producer and consumer process threads */
    std::thread t1(producer_process), t2(consumer_process);

    /* wait for both threads to complete before comparisons */
    t1.join();
    t2.join();

    /* iterating through the contents of both integer arrays */
    for (size_t i = 0; i < NUMBER_OF_MESSAGES; i++) {

        /* printing the contents of the arrays to terminal */
        std::cout << "[" << i << "] " << PRODUCED_MESSAGES[i]
            << " <-> " << CONSUMED_MESSAGES[i] << std::endl;

        /* inform user and exit program if comparison fails */
        if (PRODUCED_MESSAGES[i] != CONSUMED_MESSAGES[i]) {
            std::cout << "SYNCHRONIZATION FAILURE!" << std::endl;
            std::cin.ignore(); return -1;
        }
    }


    /* inform user of succesful message passing results */
    std::cout << "ACTION COMPLETED SUCCESFULLY" << std::endl;
    std::cin.ignore(); return 0;
}

这是Buffer / Semaphore实现。我试着把这本书跟到T,并且考虑到几乎每次运行它都是成功的(除了在运行时抛出错误)我认为同步性在这里是稳定的。

struct LogicalRingBuffer{
private:
    /* the standard size of the ring buffer */
    const static size_t SIZEOF_RING_BUFFER = 10;

    /* buffer array and pointers for iteration */
    int BUFFER[SIZEOF_RING_BUFFER], *head, *tail;

    /* inserts data into the buffer, and recycles the tail pointer */
    void push(int data) {
        /* insert data into the buffer, increment tail pointer */
        *tail = data;
        ++tail;
        /* if tail pointing at end of BUFFER, reset to the front */
        if (tail == BUFFER + (SIZEOF_RING_BUFFER - 1)) tail = BUFFER;
    }

    /* removes data from the buffer, and recycles the head pointer */
    int pull() {
        /* remove data from the buffer, increment head pointer */
        int R = *head;
        ++head;
        /* if head pointing at end of BUFFER, reset to the front */
        if (head == BUFFER + (SIZEOF_RING_BUFFER - 1)) head = BUFFER;
        /* return the integer data value */
        return R;
    }

    struct Semaphore {

        /* the counting value, number of resources */
        int count{ NULL };

        /* examines resources, holds until ready */
        void wait() {
            while (count <= 0); //busy wait
            --count;
        }

        /* releases aquired resource (increment) */
        void signal() {
            ++count;
        }

    } empty, full, mutex; /* Semaphores for Synchronization */

public:
    /* initializer for LogicalRingBuffer struct */
    LogicalRingBuffer() {
        head = tail = BUFFER;               // all pointers at BUFFER[0]
        empty.count = SIZEOF_RING_BUFFER;   // number of open positions
        mutex.count = 1;                    // a binary semaphore, mutex
        full.count = 0;                     // number of used positions
    }

    /* semaphore synchronized insertion of data */
    void insert(int data) {
        empty.wait();   // decrements available positions in buff
        mutex.wait();   // waits to gain mutual exlusion lock
        push(data);     // pushes the data into the ring buff
        mutex.signal(); // releases the mutual exclusion lock
        full.signal();  // increments count of buffered datums
    }

    /* semaphore synchronized removal of data */
    int remove() {
        int data{ NULL };   // initialize return data
        full.wait();    // decrements count of buffered items
        mutex.wait();   // waits to gain mutual exlusion lock
        data = pull();  // pulls the data from the ring buff
        mutex.signal(); // releases the mutual exlusion lock
        empty.signal(); // increments avilable positions in buff
        return data;        // return integer data
    }

};

我想我只想让它在没有任何打嗝的情况下运行,所以我在这里错过了一些东西吗?因为我相当肯定逻辑是正确的,这可能是Visual Studio所做的,或者谁知道...

2 个答案:

答案 0 :(得分:0)

您创建的两个线程都试图通过调用Semaphore::wait()来锁定互斥锁。但是,没有什么能阻止线程同时读取和写入互斥锁的count变量。这会导致未定义的行为,这可能导致您看到的系统错误。您在实现中处于正确的轨道上,但在某些时候程序必须转到操作系统以获得实际的线程安全性。您需要使用标准库的线程安全机制来保护对此变量的并发访问或操作系统的低级机制,如Windows的critical sections

C ++ 11的标准库实现std::mutex,可用于保护对线程之间共享的变量的并发访问。但是,它的实现可能与您尝试自己实现的实现一样高,因此放弃您自己的实现以支持标准库更有意义。

答案 1 :(得分:0)

这是我解决问题的方法,在Cygwin GCC中编译:{{1​​}}并重写g++ *.cpp -std=c++11 -w方法如下:

Semaphore::wait()

我计划运行更多测试,但到目前为止,我在使用线程睡眠时遇到了0个冲突。