簡體   English   中英

如何在C ++中異步讀取/寫入?

[英]How to asynchronously read/write in C++?

如何使用C ++中的專用讀/寫線程將一個流復制到另一個流?

假設我有這些方法(不是真正的,但是為了說明這一點)來讀取/寫入數據。 這些讀/寫功能可以代表任何內容(網絡/文件/ USB /串行/等)。

// returns the number of bytes read
void read(char* buffer, int bufferSize, int* bytesRead);
// returns the number of bytes written
void write(char* buffer, int bufferSize, int* bytesWritten);

該解決方案還應該是便攜式的。

注意 :我知道Windows具有FILE_FLAG_OVERLAPPED功能,但這假定讀/寫是文件IO。 記住,這些讀/寫方法可以代表任何東西。

通常,每個方向只有一個線程在讀和寫之間交替。

這是我想出的解決方案。

標頭

#pragma once

#include <stdlib.h>
#include <queue>
#include <mutex>
#include <thread>
#include <chrono>
#include <list>
#include <thread>

#define ASYNC_COPY_READ_WRITE_SUCCESS 0

struct BufferBlock;

struct ReadStream
{
    // read a stream to a buffer.
    // return non-zero if error occured
    virtual int read(char* buffer, int bufferSize, int* bytesRead) = 0;
};

struct WriteStream
{
    // write a buffer to a stream.
    // return non-zero if error occured
    virtual int write(char* buffer, int bufferSize, int* bytesWritten) = 0;
};

class BufferBlockManager
{

public:

    BufferBlockManager(int numberOfBlocks, int bufferSize);
    ~BufferBlockManager();

    void enqueueBlockForRead(BufferBlock* block);
    void dequeueBlockForRead(BufferBlock** block);
    void enqueueBlockForWrite(BufferBlock* block);
    void dequeueBlockForWrite(BufferBlock** block);
    void resetState();

private:

    std::list<BufferBlock*> blocks;
    std::queue<BufferBlock*> blocksPendingRead;
    std::queue<BufferBlock*> blocksPendingWrite;
    std::mutex queueLock;
    std::chrono::milliseconds dequeueSleepTime;

};

void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult);

CPP

#include "AsyncReadWrite.h"

struct BufferBlock
{
    BufferBlock(int bufferSize) : buffer(NULL)
    {
        this->bufferSize = bufferSize;
        this->buffer = new char[bufferSize];
        this->actualSize = 0;
        this->isLastBlock = false;
    }
    ~BufferBlock()
    {
        this->bufferSize = 0;
        free(this->buffer);
        this->buffer = NULL;
        this->actualSize = 0;
    }
    char* buffer;
    int bufferSize;
    int actualSize;
    bool isLastBlock;
};

BufferBlockManager::BufferBlockManager(int numberOfBlocks, int bufferSize)
{
    dequeueSleepTime = std::chrono::milliseconds(100);
    for (int x = 0; x < numberOfBlocks; x++)
    {
        BufferBlock* block = new BufferBlock(bufferSize);
        blocks.push_front(block);
        blocksPendingRead.push(block);
    }
}

BufferBlockManager::~BufferBlockManager()
{
    for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
        delete (*iterator);
    }
}

void BufferBlockManager::enqueueBlockForRead(BufferBlock* block)
{
    queueLock.lock();

    block->actualSize = 0;
    block->isLastBlock = false;
    blocksPendingRead.push(block);

    queueLock.unlock();
}

void BufferBlockManager::dequeueBlockForRead(BufferBlock** block)
{
WAITFOR:
    while (blocksPendingRead.size() == 0)
        std::this_thread::sleep_for(dequeueSleepTime);

    queueLock.lock();

    if (blocksPendingRead.size() == 0)
    {
        queueLock.unlock();
        goto WAITFOR;
    }

    *block = blocksPendingRead.front();

    blocksPendingRead.pop();

    queueLock.unlock();
}

void BufferBlockManager::enqueueBlockForWrite(BufferBlock* block)
{
    queueLock.lock();

    blocksPendingWrite.push(block);

    queueLock.unlock();
}

void BufferBlockManager::dequeueBlockForWrite(BufferBlock** block)
{
WAITFOR:
    while (blocksPendingWrite.size() == 0)
        std::this_thread::sleep_for(dequeueSleepTime);

    queueLock.lock();

    if (blocksPendingWrite.size() == 0)
    {
        queueLock.unlock();
        goto WAITFOR;
    }

    *block = blocksPendingWrite.front();

    blocksPendingWrite.pop();

    queueLock.unlock();
}

void BufferBlockManager::resetState()
{
    queueLock.lock();

    blocksPendingRead = std::queue<BufferBlock*>();
    blocksPendingWrite = std::queue<BufferBlock*>();

    for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
        (*iterator)->actualSize = 0;
    }

    queueLock.unlock();
}

struct AsyncCopyContext
{
    AsyncCopyContext(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream)
    {
        this->bufferBlockManager = bufferBlockManager;
        this->readStream = readStream;
        this->writeStream = writeStream;
        this->readResult = ASYNC_COPY_READ_WRITE_SUCCESS;
        this->writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
    }
    BufferBlockManager* bufferBlockManager;
    ReadStream* readStream;
    WriteStream* writeStream;
    int readResult;
    int writeResult;
};

void ReadStreamThread(AsyncCopyContext* asyncContext)
{
    int bytesRead = 0;
    BufferBlock* readBuffer = NULL;
    int readResult = ASYNC_COPY_READ_WRITE_SUCCESS;

    while (
        // as long there hasn't been any write errors
        asyncContext->writeResult == ASYNC_COPY_READ_WRITE_SUCCESS
        // and we haven't had an error reading yet
        && readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
    {
        // let's deque a block to read to!
        asyncContext->bufferBlockManager->dequeueBlockForRead(&readBuffer);

        readResult = asyncContext->readStream->read(readBuffer->buffer, readBuffer->bufferSize, &bytesRead);
        readBuffer->actualSize = bytesRead;
        readBuffer->isLastBlock = bytesRead == 0;

        if (readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
        {
            // this was a valid read, go ahead and queue it for writing
            asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
        }
        else
        {
            // an error occured reading
            asyncContext->readResult = readResult;

            // since an error occured, lets queue an block to write indicatiting we are done and there are no more bytes to read
            readBuffer->isLastBlock = true;
            readBuffer->actualSize = 0;

            asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
        }

        if (readBuffer->isLastBlock) return;
    }
}

void WriteStreamThread(AsyncCopyContext* asyncContext)
{
    int bytesWritten = 0;
    BufferBlock* writeBuffer = NULL;
    int writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
    bool isLastWriteBlock = false;

    while (
        // as long as there are no errors during reading
        asyncContext->readResult == ASYNC_COPY_READ_WRITE_SUCCESS
        // and we haven't had an error writing yet
        && writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
    {
        // lets dequeue a block for writing!
        asyncContext->bufferBlockManager->dequeueBlockForWrite(&writeBuffer);

        isLastWriteBlock = writeBuffer->isLastBlock;

        if (writeBuffer->actualSize > 0)
            writeResult = asyncContext->writeStream->write(writeBuffer->buffer, writeBuffer->actualSize, &bytesWritten);

        if (writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
        {
            asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
            if (isLastWriteBlock) return;
        }
        else
        {
            asyncContext->writeResult = writeResult;
            asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
            return;
        }
    }
}

void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult)
{
    AsyncCopyContext asyncContext(bufferBlockManager, readStream, writeStream);
    std::thread readThread(ReadStreamThread, &asyncContext);
    std::thread writeThread(WriteStreamThread, &asyncContext);

    readThread.join();
    writeThread.join();

    *readResult = asyncContext.readResult;
    *writeResult = asyncContext.writeResult;
}

用法

#include <stdio.h>
#include <tchar.h>
#include "AsyncReadWrite.h"

struct ReadTestStream : ReadStream
{
    int readCount = 0;
    int read(char* buffer, int bufferSize, int* bytesRead)
    {
        printf("Starting read...\n");

        memset(buffer, bufferSize, 0);

        if (readCount == 10)
        {
            *bytesRead = 0;
            return 0;
        }

        // pretend this function takes a while!
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        char buff[100];
        sprintf_s(buff, "This is read number %d\n", readCount);
        strcpy_s(buffer, sizeof(buff), buff);

        *bytesRead = strlen(buffer);

        readCount++;

        printf("Finished read...\n");

        return 0;
    }
};

struct WriteTestStream : WriteStream
{
    int write(char* buffer, int bufferSize, int* bytesWritten)
    {
        printf("Starting write...\n");

        // pretend this function takes a while!
        std::this_thread::sleep_for(std::chrono::milliseconds(500));

        printf(buffer);

        printf("Finished write...\n");

        return 0;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    BufferBlockManager bufferBlockManager(5, 4096);
    ReadTestStream readStream;
    WriteTestStream writeStream;
    int readResult = 0;
    int writeResult = 0;

    printf("Starting copy...\n");

    AsyncCopyStream(&bufferBlockManager, &readStream, &writeStream, &readResult, &writeResult);

    printf("Finished copy... readResult=%d writeResult=%d \n", readResult, writeResult);

    getchar();

    return 0;
}

編輯 :我將解決方案放入此處的GitHub存儲庫中。 如果您想使用此代碼,請參考存儲庫,因為它可能比此答案更新得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM