• 双缓冲队列,生产者消费者模式


    #include "stdafx.h"
    #include <windows.h>
    #include <process.h>

    template <typename T>
    class CircularQueue
    {
    public:
    explicit CircularQueue(int capacity) : m_capacity(capacity), m_head(0), m_tail(0)
    {
    m_array = new T[m_capacity + 1];
    }

    ~CircularQueue()
    {
    delete[] m_array;
    m_capacity = m_head = m_tail = 0;
    }

    bool IsFull()
    {
    int offset = (m_tail + 1) % (m_capacity + 1);

    return (offset == m_head);
    }

    bool IsEmpty()
    {
    return (m_tail == m_head);
    }

    void Push(const T& item)
    {
    if ( !IsFull() )
    {
    m_array[m_tail] = item;
    m_tail = (m_tail + 1) % (m_capacity + 1);
    }
    }

    T Pop()
    {
    if ( IsEmpty() )
    {
    return T();
    }

    int index = m_head;
    m_head = (m_head + 1) % (m_capacity + 1);

    return m_array[index];
    }

    private:
    T* m_array; // 缓冲区队列
    int m_capacity; // 队列最大存储容量
    int m_head; // 队列头指针
    int m_tail; // 队列尾指针
    };

    // 同步队列
    template <typename T>
    struct SynQueue
    {
    SynQueue(int size) : buffer(size)
    {
    synEventHandle = CreateEvent(NULL, FALSE, FALSE, 0);
    }

    ~SynQueue()
    {
    CloseHandle(synEventHandle);
    }

    operator HANDLE()
    {
    return synEventHandle;
    }

    CircularQueue<T> buffer; // 缓冲区
    HANDLE synEventHandle; // 同步事件
    };

    class PacketQueue
    {
    public:
    explicit PacketQueue(int size) : m_readQueue(size),
    m_sendQueue(size), m_bFreezeQueue(false)
    {
    }

    // 刷新读取缓冲区,使得发送线程有机会接管读取缓冲区,在读取完毕后调用
    void Flush()
    {
    SetEvent(m_readQueue);
    SetEvent(m_sendQueue);
    }

    // 冻结缓冲区
    void FreezeQueue()
    {
    m_bFreezeQueue = true;
    }

    // 从发送缓冲区取出一个数据包
    int Popup()
    {
    static SynQueue<int>* pSendQueue = &m_sendQueue;
    static SynQueue<int>* pReadQueue = &m_readQueue;

    // 判断发送缓冲区数据包是否为空
    if ( pSendQueue->buffer.IsEmpty() )
    {
    // 释放当前发送缓冲区
    SetEvent(pSendQueue->synEventHandle);

    // 得到当前读取缓冲区
    pReadQueue = ExchangeQueue(pSendQueue);

    // 接管当前读取缓冲区的拥有权
    WaitForSingleObject(pReadQueue->synEventHandle, INFINITE);

    // 接管读取缓冲区
    pSendQueue = pReadQueue;
    }

    // 从缓冲区取出一个数据包
    return pSendQueue->buffer.Pop();
    }

    bool Push(int item)
    {
    static SynQueue<int>* pReadQueue = &m_readQueue;
    static SynQueue<int>* pSendQueue = &m_sendQueue;

    if ( m_bFreezeQueue )
    {
    return false;
    }

    // 判断读取缓冲区是否数据已填满
    if ( pReadQueue->buffer.IsFull() )
    {
    // 释放当前缓冲区拥有权
    SetEvent(pReadQueue->synEventHandle);

    pSendQueue = ExchangeQueue(pReadQueue);

    // 等待接管另一个缓冲区的拥有权
    WaitForSingleObject(pSendQueue->synEventHandle, INFINITE);

    // 接管另一个缓冲区
    pReadQueue = pSendQueue;
    }

    // 插入数据包
    pReadQueue->buffer.Push(item);

    return true;
    }

    private:
    // 交换缓冲区
    SynQueue<int>* ExchangeQueue(SynQueue<int>* queue)
    {
    if ( (queue != &m_readQueue) && (queue != &m_sendQueue) )
    {
    return 0;
    }

    return (queue == &m_readQueue ? &m_sendQueue : &m_readQueue);
    }

    private:
    // 双缓冲队列
    SynQueue<int> m_readQueue; // 数据读取缓冲区
    SynQueue<int> m_sendQueue; // 数据发送缓冲区

    bool m_bFreezeQueue; // 发送线程出现致命错误,冻结缓冲区
    };

    // 读取线程入口函数
    unsigned WINAPI ReadThreadEntry( PVOID param )
    {
    PacketQueue* queue = (PacketQueue*)param;

    // 插入100个数据包
    for ( int i = 0; i < 10000; ++i )
    {
    Sleep(100); // 读取间隔时间

    if ( !queue->Push(i) )
    {
    goto exit;
    }

    printf("intput data: %d\n", i);
    }

    exit:

    // 插入结束包
    queue->Push(-1);
    printf("input end data\n");

    // 刷新读缓冲区
    queue->Flush();
    printf("flush read buffer\n");

    return 0;
    }

    // 发送线程入口函数
    unsigned WINAPI SendThreadEntry( PVOID param )
    {
    PacketQueue* queue = (PacketQueue*)param;

    // 循环读取数据包
    for (;;)
    {
    //queue->FreezeQueue();
    //return 0;

    int item = queue->Popup();
    if ( -1 == item )
    {
    printf("send thread fetch end data\n");
    break; // 遇到结束包
    }

    //Sleep(150);
    printf("send thread fetch data: %d\n", item);
    }

    return 0;
    }

    int main(int argc, char* argv[])
    {
    PacketQueue queue(50);

    // 启动读取线程
    HANDLE hReadThread = (HANDLE)_beginthreadex(NULL, 0, ReadThreadEntry, (void*)&queue, 0, NULL);

    // 启动发送线程
    HANDLE hSendThread = (HANDLE)_beginthreadex(NULL, 0, SendThreadEntry, (void*)&queue, 0, NULL);

    DWORD T1 = GetTickCount();

    WaitForSingleObject(hReadThread, INFINITE);
    WaitForSingleObject(hSendThread, INFINITE);

    printf("total time: %d", GetTickCount() - T1);

    return 0;
    }
  • 相关阅读:
    四个好看的CSS样式表格
    POJ 2255 Tree Recovery
    黑马程序猿_2014 7月 我使用多线程体验
    Dos命令将合并两个文本文件的内容
    栈和堆之间的差(他转身无数的文章)
    【Espruino】NO.12 加速度计演示
    MySQL进口.sql文件和常用命令
    typedef和define具体的具体差异
    muduo网络图书馆评测
    Web采矿技术
  • 原文地址:https://www.cnblogs.com/csuchao/p/2409049.html
Copyright © 2020-2023  润新知