尝试通过大量不同的函数调用来理解asynchrouny

时间:2017-12-07 17:58:37

标签: c++ multithreading winapi asynchronous wininet

我开始学习异步方法,并且 遇到问题,请帮帮我。

目的是:从某个地方获取char数据,然后用它做一些事情(在我的情况下使用按钮上的文本)。下面固定的代码非常慢。最慢的时刻是数据获取:事实是get(int id)函数通过WinInet(同步)从互联网加载数据,发送Post方法,并返回答案。

void some_func()
{
for(int i(0);i<10;i++)
   for(int q(0);q<5;q++)
     {
       char data[100];
       strcpy(data, get(i,q)); // i, q - just some identifier data

       button[5*i+(q+1)]=new Button(data);
     }
}

第一个问题:

如何解决(一般来说,我的意思是,如果get与互联网无关,但运行缓慢)?我只有一个愚蠢的想法:在每个单独的线程中运行get。如果这是正确的方式 - 我该怎么做?因为,每个get函数都创建了50个线程调用,这是错误的。 50个get函数?

第二个问题

如何使用WinInet实现它?有红色的MSDN,但它对我来说太难了,至于更新,也许你更明白地解释一下?

由于

1 个答案:

答案 0 :(得分:0)

对于异步编程,您需要创建一些将维护状态的对象 - 在当前情况下iq必须不是函数的局部变量,而是对象的成员,强制引用计数到对象。和通常的文件(套接字)句柄等

函数some_func()必须有另一种模式。它必须是对象的成员函数。并且它不能在循环中调用异步get。在致电get后,它必须退出。当由get启动的异步操作完成时 - 必须调用一些回调(如果失败启动异步操作,则需要自己调用带有错误代码的回调)。在回调中,您将拥有指向对象并使用它的指针 - 调用some_func()。所以some_func()必须在开始处理先前get调用的结果 - 检查错误,处理收到的数据,如果没有错误。比调整对象状态(在您的情况下为iq),如果需要,请再次调用get。并且为了启动这一切 - 需要第一次直接致电get

begin -> get() -> .. callback .. -> some_func() -> exit
          ^                             ┬
          └─────────────────────────────┘   

一些演示示例(带异步读取文件)

struct SOME_OBJECT 
{
    LARGE_INTEGER _ByteOffset;
    HANDLE _hFile;
    LONG _dwRef;
    int _i, _q;

    SOME_OBJECT()
    {
        _i = 0, _q = 0;
        _dwRef = 1;
        _ByteOffset.QuadPart = 0;
        _hFile = 0;
    }

    void beginGet();

    void DoSomething(PVOID pvData, DWORD_PTR cbData)
    {
        DbgPrint("DoSomething<%u,%u>(%x, %p)\n", _i, _q, cbData, pvData);
    }

    // some_func
    void OnComplete(DWORD dwErrorCode, PVOID pvData, DWORD_PTR cbData)
    {
        if (dwErrorCode == NOERROR)
        {
            DoSomething(pvData, cbData);

            if (++_q == 5)
            {
                _q = 0;

                if (++_i == 10)
                {
                    return ;
                }
            }

            _ByteOffset.QuadPart += cbData;

            beginGet();
        }
        else
        {
            DbgPrint("OnComplete - error=%u\n", dwErrorCode);
        }
    }

    ~SOME_OBJECT()
    {
        if (_hFile) CloseHandle(_hFile);
    }

    void AddRef() { InterlockedIncrement(&_dwRef); }

    void Release() { if (!InterlockedDecrement(&_dwRef)) delete this; }

    ULONG Create(PCWSTR FileName);
};

struct OPERATION_CTX : OVERLAPPED 
{
    SOME_OBJECT* _pObj;
    BYTE _buf[];

    OPERATION_CTX(SOME_OBJECT* pObj) : _pObj(pObj)
    {
        pObj->AddRef();
        hEvent = 0;
    }

    ~OPERATION_CTX()
    {
        _pObj->Release();
    }

    VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
    {
        _pObj->OnComplete(dwErrorCode, _buf, dwNumberOfBytesTransfered);

        delete this;
    }

    static VOID CALLBACK _CompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* lpOverlapped)
    {
        static_cast<OPERATION_CTX*>(lpOverlapped)->CompletionRoutine(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
    }

    void CheckResult(BOOL fOk)
    {
        if (!fOk)
        {
            ULONG dwErrorCode = GetLastError();

            if (dwErrorCode != ERROR_IO_PENDING)
            {
                CompletionRoutine(dwErrorCode, 0);
            }
        }
    }

    void* operator new(size_t cb, size_t ex)
    {
        return ::operator new(cb + ex);
    }

    void operator delete(PVOID pv)
    {
        ::operator delete(pv);
    }
};

ULONG SOME_OBJECT::Create(PCWSTR FileName)
{
    HANDLE hFile = CreateFile(FileName, FILE_READ_DATA, FILE_SHARE_READ, 0, 
        OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        _hFile = hFile;

        if (BindIoCompletionCallback(hFile, OPERATION_CTX::_CompletionRoutine, 0)) 
        {
            return NOERROR;
        }
    }

    return GetLastError();
}

void SOME_OBJECT::beginGet()
{
    const ULONG cbRead = 0x1000;

    if (OPERATION_CTX* ctx = new(cbRead) OPERATION_CTX(this))
    {
        ctx->Offset = _ByteOffset.LowPart;
        ctx->OffsetHigh = _ByteOffset.HighPart;
        ctx->CheckResult(ReadFile(_hFile, ctx->_buf, cbRead, 0, ctx));
    }
}

void ADemo(PCWSTR FileName)
{
    if (SOME_OBJECT* pObj = new SOME_OBJECT)
    {
        if (!pObj->Create(FileName))
        {
            pObj->beginGet();
        }
        pObj->Release();
    }
}