Home > database >  How do I implement std::thread the right way with loops?
How do I implement std::thread the right way with loops?

Time:06-13

I´m trying to create a multithread part in my program, where a loop creates multiple threads, that get a vector consisting of objects along some integers and the vector which holds the results. The problem is I can´t seem to wrap my head around how the thread part works, I tried different things but all end in the same three errors. This is where I don´t know how to proceed:

std::thread thread_superdiecreator;

for (int64_t i = 0; i < dicewithside.back().sides; i  ) {
    thread_superdiecreator(func_multicreator(dicewithside, i, amount, lastdiepossibilities, superdie));
}

term does not evalutate to a function taking 1 arguments

I tried this:

thread_superdiecreator(func_multicreator, dicewithside, i, amount, lastdiepossibilities, superdie);

call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type

And this:

std::thread thread_superdiecreator(func_multicreator, dicewithside, i, amount, lastdiepossibilities, superdie);

Invoke error in thread.

The whole code snippet:

#pragma once
#include <mutex>
#include <thread>
#include <algorithm>
#include "class_Diewithside.h"
#include "struct_Sortedinput.h"
#include "func_maximumpossibilities.h"

std::mutex superdielock;


void func_multicreator(std::vector<Diewithside> dicewithside, int64_t lastdieside, int64_t size, int64_t lastdiepossibilities, std::vector<int64_t> &superdie) {
    
    // Set the last die side to number of the thread
    dicewithside[size-1].dieside = lastdieside;
    //
    std::vector<int64_t> partsuperdie;
    partsuperdie.reserve(lastdiepossibilities);

    // Calculate all possible results of all dice thrown with the last one set
    for (int64_t i = 0; i < lastdiepossibilities; i  ) {
        
        // Reset the result
        int64_t result = 0;
        for (int64_t j = 0; j < size; j  ) {
            result  = dicewithside[j].alleyes[dicewithside[j].dieside];
        }
        partsuperdie.push_back(result);

        //
        for (int64_t j = 0; j < size - 1; j  ) {
            if (dicewithside[j].dieside == dicewithside[j].sides - 1) {
                dicewithside[j].dieside = 0;
            }
            else {
                dicewithside[j].dieside  = 1;
                break;
            }
        }
    }

    superdielock.lock();
    for (int64_t i = 0; i < lastdiepossibilities; i  ) {
        superdie.push_back(partsuperdie[i]);
    }
    superdielock.unlock();
}



// The function superdie creates an array that holds all possible outcomes of the dice thrown
std::vector<int64_t> func_superdiecreator(sortedinput varsortedinput) {

    // Get the size of the diceset vector and create a new vector out of class Diewithside
    int64_t size = varsortedinput.dicesets.size();
    std::vector<Diewithside> dicewithside;

    // Initialize the integer amount and iterate through all the amounts of vector dicesets adding them together to set the vector dicewithside reserve
    int64_t amount = 0;
    for (int64_t i = 0; i < size; i  ) {
        amount  = varsortedinput.dicesets[i].amount;
    }
    dicewithside.reserve(amount);

    // Fill the new vector dicewithside with each single die and add the starting value of 0
    for (int64_t i = 0; i < size; i  ) {
        for (int64_t j = 0; j < varsortedinput.dicesets[i].amount; j  ) {
            dicewithside.push_back(Diewithside{varsortedinput.dicesets[i].plusorminus, varsortedinput.dicesets[i].sides, varsortedinput.dicesets[i].alleyes, 0});
        }
    }

    // Get the maximum possibilities and divide by sides of the last die to get the amount of iterations each thread has to run
    int64_t maximumpossibilities = func_maximumpossibilities(varsortedinput.dicesets, size);
    int64_t lastdiepossibilities = maximumpossibilities / dicewithside[amount-1].sides;

    // Multithread calculate all possibilities and save them in array
    std::vector<int64_t> superdie;
    superdie.reserve(maximumpossibilities);

    std::thread thread_superdiecreator;
    
    for (int64_t i = 0; i < dicewithside.back().sides; i  ) {
        thread_superdiecreator(func_multicreator(dicewithside, i, amount, lastdiepossibilities, superdie));
    }

    thread_superdiecreator.join();

    return superdie;

Thanks for any help!

CodePudding user response:

You indeed need to create the thread using the third alternative mentioned in the question, i.e. use the constructor of std::thread to start the thread.

The issue with this approach is the fact the last parameter of func_multicreator being a lvalue reference: std::thread creates copies of parameters and moves those copies during for calling the function on the background thread, and an rvalue reference cannot be implicitly converted to an lvalue reference. You need to use std::reference_wrapper here to be able to "pass" an lvalue reference to the thread.

You should join every thread created so you need to create a collection of threads.

Simplified example:

(The interesting stuff is between the ---... comments.)

struct Diewithside
{
    int64_t sides;
};

void func_multicreator(std::vector<Diewithside> dicewithside, int64_t lastdieside, int64_t size, int64_t lastdiepossibilities, std::vector<int64_t>& superdie)
{
}

std::vector<int64_t> func_superdiecreator() {
    std::vector<Diewithside> dicewithside;

    // Initialize the integer amount and iterate through all the amounts of vector dicesets adding them together to set the vector dicewithside reserve
    int64_t amount = 0;
    int64_t lastdiepossibilities = 0;

    std::vector<int64_t> superdie;

    // -----------------------------------------------

    std::vector<std::thread> threads;

    for (int64_t i = 0; i < dicewithside.back().sides; i  ) {
        // create thread using constructor std::thread(func_multicreator, dicewithside, i, amount, lastdiepossibilities, std::reference_wrapper(superdie));
        threads.emplace_back(func_multicreator, dicewithside, i, amount, lastdiepossibilities, std::reference_wrapper(superdie));
    }

    for (auto& t : threads)
    {
        t.join();
    }

    // -----------------------------------------------

    return superdie;
}

CodePudding user response:

std::thread thread_superdiecreator;

A single std::thread object always represents a single execution threads. You seem to be trying to use this single object to represent multiple execution threads. No matter what you will try, it won't work. You need multiple std::thread objects, one for each execution thread.

thread_superdiecreator(func_multicreator, dicewithside, i, amount, lastdiepossibilities, superdie);

An actual execution thread gets created by constructing a new std::thread object, and not by invoking it as a function.

Constructing an execution thread object corresponds to the creation of a new execution thread, it's just that simple. And the simplest way to have multiple execution threads is to have a vector of them.

std::vector<std::thread> all_execution_threads.

With that in place, creating a new execution thread involves nothing more than constructing a new std::thread object and moving it into the vector. Or, better yet, emplace it directly:

all_execution_threads.emplace_back(
     func_multicreator, dicewithside, i,
     amount, lastdiepossibilities, superdie
);

This presumes that everything else is correct: func_multicreator agrees with the following parameters, none of them are passed by reference (you need to fix this, at least, your attempt to pass a reference into a thread function will not work), leaving dangling references behind, all access to all objects by multiple execution threads are correctly synchronized, with mutexes, and all the other usual pitfalls when working with multiple execution threads. But this covers the basics of creating some unspecified number of multiple, concurrent, execution threads. When all and said and done you end up with a std::vector of std::threads, one for each actual execution thread.

  • Related