Home Trying to understand the asynchrouny with a big amount of different function calls
Reply: 1

Trying to understand the asynchrouny with a big amount of different function calls

Артур Клочко
1#
Артур Клочко Published in 2017-12-07 17:58:36Z

I've started learing an asynchronous aproach, and encountered a problem, help me with it.

The purpose is: get from somewhere a char data, and after that do something with it(using as text on the button, in my case). The code, that is pinned below is very slow. The most slowiest moment is a data getting: the fact is that the get(int id) function loads data from internet via WinInet(synchronously), sending the Post methods, and returning the answer.

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);
     }
}

The first question:

How should it be solved(generaly, I mean, if get has nothing to do with the internet, but runs slow)? I have only one, stupid idea: run get in every separate thread. If it's the right way - how should I do that? Cause, it's wrong to, created 50 threads call from each the get function. 50 get functions?

Second Question

How to realize it with WinInet? Have red MSDN, but it too hardly for me, as for newer, maybe you explain it more simlier?

Thanks

RbMm
2#
RbMm Reply to 2017-12-07 21:00:31Z

for asynchronous programming you need create some object which will be maintain state - in current case i and q must be not local variables of function but members of object, mandatory reference count to object. and usual file(socket) handle , etc.

function some_func() must have another pattern. it must be member function of object. and it must not call asynchronous get in loop. after call get it must just exit. when asynchronous operation, initiated by get, will be finished - some your callback must be called (if failed initiate asynchronous operation you need yourself just call this callback with error code). in callback you will be have pointer to your object and using it - call some_func(). so some_func() must at begin handle result of previous get call - check for error, handle received data, if no error. than adjust object state (in your case i and q) and if need - call get again. and for initiated all this - need first time call get direct:

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

some demo example (with asynchronous read file)

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();
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.327837 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO