如何以多线程方式编码位图块?

How can encode bitmap blocks in a multi-threaded fashion?

我正在尝试对位图图像块进行编码并将其保存到内存中(在矢量内部)。一切正常,直到我尝试以多线程方式执行此操作。我不断收到以下错误:

Error C2672 'std::invoke': no matching overloaded function found

Error C2893 Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept()'

我的代码是在 main() 中调用的简单屏幕截图 class,这是我尝试进行多线程处理的地方:

bool Screenshot::threadfunc(Gdiplus::Bitmap* bmp, int i, int j, int x, int y, int bw, int bh, std::vector<std::vector< std::vector<BYTE> >> blocksBmpBytesMatrix, std::string dataFormat)
{
    Gdiplus::Bitmap* tile = bmp->Clone(x, y, bw, bh, PixelFormat24bppRGB);

    // write to IStream
    IStream* istream = nullptr;
    CreateStreamOnHGlobal(NULL, TRUE, &istream);

    // define encoding
    CLSID clsid;
    CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid);

    Gdiplus::Status status = tile->Save(istream, &clsid, NULL);
    if (status != Gdiplus::Status::Ok)
        std::wcout << "ERROR" << std::endl;
    return false;

    // get memory handle associated with istream
    HGLOBAL hg = NULL;
    GetHGlobalFromStream(istream, &hg);

    // copy IStream to buffer
    int bufsize = GlobalSize(hg);
    blocksBmpBytesMatrix[i][j].resize(bufsize);

    // lock & unlock memory
    LPVOID pimage = GlobalLock(hg);
    memcpy(&blocksBmpBytesMatrix[i][j][0], pimage, bufsize);
    GlobalUnlock(hg);
    istream->Release();
    return true;
};

bool Screenshot::divideIntoBlocks(HWND chwnd, int screenshotId, RECT rcMonitors, int blockHeight, int blockWidth)
{
    Gdiplus::Bitmap bmp(hbwindow, nullptr);
    int nrows = height / blockHeight + 1 * int((height % blockHeight) != 0);
    int ncols = width / blockWidth + 1 * int((width % blockWidth) != 0);

    for (int i = 0; i < nrows; i++)
    {
        for (int j = 0; j < ncols; j++)
        {
            // compute block coordinates and dimensions
            int x = j * blockWidth;
            int y = i * blockHeight;
            int bw = ((x + blockWidth) > width) * (width % blockWidth) + ((x + blockWidth) <= width) * blockWidth;
            int bh = ((y + blockHeight) > height) * (height % blockHeight) + ((y + blockHeight) <= height) * blockHeight;

            // append to vecs
            blocksInfo.push_back({ i, j, x, y, bw, bh });
        }
    }

    std::vector<std::thread*> pool(nrows * ncols);
    for (auto& ij : blocksInfo)
    {
        std::cout << ij.size() << " : " << ij[0] << "," << ij[1] << "," << ij[2] << "," << ij[3] << "," << ij[4] << "," << ij[5] << std::endl;
        std::thread t(&Screenshot::threadfunc, &bmp, ij[0], ij[1], ij[2], ij[3], ij[4], ij[5], this->blockPngBytesMatrix, "png");
        pool.push_back(&t);
    }
    for (auto& t : pool) { t->join(); }

    return true;
}

知道我在这里做错了什么吗?或者如果我能(我怎么能)做这个多线程编码?

编辑:这是Screenshot.h

class Screenshot
{
    public:
        // screenshot dimensions and coordinates
        int width, height;
        int screenx, screeny;

        // inti coordinates and dimensions vectors
        std::vector< std::vector<int>> blocksInfo;

        // init data matrices
        std::vector<std::vector< std::vector<BYTE> >> blocksPngBytesMatrix;

        // init screenshot bmp and hbmp 
        HBITMAP hbwindow;
        BITMAPINFOHEADER bi; 

        // init handle for display contexts
        HDC hwindowDC;
        HDC hwindowCompatibleDC;

        // constructor
        Screenshot(RECT, int, int);

        // funcs
        BITMAPINFOHEADER createBitmapHeader(int, int);
        bool capture();
        bool divideIntoBlocks(HWND, int, RECT, int, int);
        bool saveToMemory(Gdiplus::Bitmap*, std::vector<BYTE>&, std::string);
        bool threadfunc(Gdiplus::Bitmap*, int, int, int, int, int, int, std::vector<std::vector< std::vector<BYTE> >>, std::string);

        // deconstructor
        ~Screenshot();
};

GDI+ 中有一个锁可以防止两个线程使用相同的图形对象或相同的位图。无论哪个线程先抢到它,另一个都会异常死掉。

参考:GDI+ objects and multithreading

Thread Synchronization也指出:

Some GDI+ methods return ObjectBusy if a thread attempts to call a method while another thread is executing a method on the same object. Do not try to synchronize access to an object based on the ObjectBusy return value.

相反,每次访问成员或调用对象的方法时,将调用置于临界区内,或使用其他一些标准同步技术。