简体   繁体   English

使用lambda表达式创建线程时,如何为每个线程提供自己的lambda表达式副本?

[英]When creating threads using lambda expressions, how to give each thread its own copy of the lambda expression?

I have been working on a program that basically used brute force to work backward to find a method using a given set of operations to reach the given number. 我一直在研究一个程序,该程序基本上使用蛮力向后工作,以找到一种使用给定操作集达到给定数量的方法。 So, for example, if I gave in a set of operations +5,-7,*10,/3, and a given number say 100(*this example probably won't come up with a solution), and also a given max amount of moves to solve (let's say 8), it will attempt to come up with a use of these operations to get to 100. This part works using a single thread which I have tested in an application. 因此,例如,如果我给出了一组运算+ 5,-7,* 10,/ 3,并且给定的数字为100(*此示例可能不会给出解决方案),要解决的最大移动量(比如说8),它将尝试使用这些操作达到100。这部分使用我在应用程序中测试过的单个线程工作。

However, I wanted it to be faster and I came to multithreading. 但是,我希望它更快,因此我开始使用多线程。 I have worked a long time to even get the lambda function to work, and after some serious debugging have realized that the solution "combo" is technically found. 我花了很长时间才能使lambda函数正常工作,并且经过认真的调试后才意识到,从技术上来说,找到了“ combo”解决方案。 However, before it is tested, it is changed. 但是,在测试之前,它已被更改。 I wasn't sure how this was possible considering the fact that I had thought that each thread was given its own copy of the lambda function and its variables to use. 考虑到我认为每个线程都拥有自己的lambda函数副本和要使用的变量,因此我不确定这是如何实现的。

In summary, the program starts off by parsing the information, then passes the information which is divided by the parser as paramaters into the array of an operation object(somewhat of a functor). 总而言之,程序首先通过解析信息开始,然后将由解析器划分为信息的信息传递到操作对象(有点像函子)的数组中。 It then uses an algorithm which generated combinations which are then executed by the operation objects. 然后使用算法生成组合,然后由操作对象执行组合。 The algorithm, in simplicity, takes in the amount of operations, assigns it to a char value(each char value corresponds to an operation), then outputs a char value. 为简单起见,该算法吸收大量运算,将其分配给一个char值(每个char值对应于一个运算),然后输出一个char值。 It generates all possible combinations. 它生成所有可能的组合。

That is a summary of how my program works. 那是我的程序如何工作的总结。 Everything seems to be working fine and in order other than two things. 除了两件事之外,其他一切似乎都正常运行。 There is another error which I have not added to the title because there is a way to fix it, but I am curious about alternatives. 我没有添加到标题中,这是另一个错误,因为有一种解决方法,但是我对替代方法感到好奇。 This way is also probably not good for my computer. 这种方式也可能对我的计算机不利。

So, going back to the problem with the lambda expression inputted with the thread as seen is with what I saw using breakpoints in the debugger. 因此,返回到通过线程输入的lambda表达式的问题,正如我在调试器中使用断点所看到的那样。 It appeared that both threads were not generating individual combos, but more rather properly switching between the first number, but alternating combos. 似乎两个线程都没有生成单独的连击,而是更恰当地在第一个数字之间切换,而是交替切换。 So, it would go 1111, 2211, rather than generating 1111, 2111.(these are generated as the previous paragraph showed, but they are done a char at a time, combined using a stringstream), but once they got out of the loop that filled the combo up, combos would get lost. 因此,它将是1111、2211,而不是生成1111、2111(它们是如上一段所示生成的,但是它们一次完成一个char,并使用stringstream组合),但是一旦它们退出循环填满了组合,组合就会迷路。 It would randomly switch between the two and never test the correct combo because combinations seemed to get scrambled randomly. 它会在两者之间随机切换,并且永远不会测试正确的组合,因为组合似乎会被随机打乱。 This I realized must have something to do with race conditions and mutual exclusion. 我意识到这一定与种族状况和相互排斥有关。 I had thought I had avoided it all by not changing any variables changed from outside the lambda expression, but it appears like both threads are using the same lambda expression. 我以为我通过不更改从lambda表达式外部更改的任何变量来避免了这一切,但是似乎两个线程都使用相同的lambda表达式。

I want to know why this occurs, and how to make it so that I can say create an array of these expressions and assign each thread its own, or something similar to that which avoids having to deal with mutual exclusion as a whole. 我想知道为什么会发生这种情况,以及如何做到这一点,以便可以说创建这些表达式的数组并为每个线程分配自己的线程,或者类似于避免整体处理互斥的内容。

Now, the other problem happens when I at the end delete my array of operation objects. 现在,当我最后删除我的操作对象数组时,会发生另一个问题。 The code which assigns them and the deleting code is shown below. 分配它们的代码和删除代码如下所示。

  operation *operations[get<0>(functions)];

  for (int i = 0; i < get<0>(functions); i++)
  {
        //creates a new object for each operation in the array and sets it to the corresponding parameter
        operations[i] = new operation(parameterStrings[i]);
  }
  delete[] operations;

The get<0>(functions) is where the amount of functions is stored in a tuple and is the number of objects to be stored in an array. get <0>(functions)是将函数的数量存储在元组中的位置,并且是要存储在数组中的对象的数量。 The paramterStrings is a vector in which the strings used as parameters for the constructor of the class are stored. paramterStrings是一个向量,其中存储了用作类的构造函数的参数的字符串。 This code results in an "Exception trace/breakpoint trap." 此代码导致“异常跟踪/断点陷阱”。 If I use "*operations" instead I get a segmentation fault in the file where the class is defined, the first line where it says "class operation." 如果我改用“ * operations”,则会在定义类的文件中出现段错误,该行的第一行显示“类操作”。 The alternative is just to comment out the delete part, but I am pretty sure that it would be a bad idea to do so, considering the fact that it is created using the "new" operator and might cause memory leaks. 另一种选择是只注释掉删除部分,但是考虑到使用“ new”运算符创建它并可能导致内存泄漏的事实,我很确定这样做是一个坏主意。

Below is the code for the lambda expression and where the corresponding code for the creation of threads. 以下是lambda表达式的代码,以及用于创建线程的相应代码。 I readded code inside the lambda expression so it could be looked into to find possible causes for race conditions. 我在lambda表达式中读取了代码,以便可以对其进行查找以找出导致竞争情况的可能原因。

auto threadLambda = [&](int thread, char *letters, operation **operations, int beginNumber) {
int i, entry[len];
        bool successfulComboFound = false;
        stringstream output;
        int outputNum;

        for (i = 0; i < len; i++)
        {
              entry[i] = 0;
        }
        do
        {
              for (i = 0; i < len; i++)
              {
                    if (i == 0)
                    {
                          output << beginNumber;
                    }

                    char numSelect = *letters + (entry[i]);

                    output << numSelect;
              }
              outputNum = stoll(output.str());
              if (outputNum == 23513511)
              {
                    cout << "strange";
              }
              if (outputNum != 0)
              {
                    tuple<int, bool> outputTuple;
                    int previousValue = initValue;
                    for (int g = 0; g <= (output.str()).length(); g++)
                    {
                          operation *copyOfOperation = (operations[((int)(output.str()[g])) - 49]);

                          //cout << copyOfOperation->inputtedValue;

                          outputTuple = (*operations)->doOperation(previousValue);
                          previousValue = get<0>(outputTuple);

                          if (get<1>(outputTuple) == false)
                          {
                                break;
                          }
                          debugCheck[thread - 1] = debugCheck[thread - 1] + 1;
                          if (previousValue == goalValue)
                          {
                                movesToSolve = g + 1;
                                winCombo = outputNum;
                                successfulComboFound = true;
                                break;
                          }
                    }
                    //cout << output.str() << ' ';
              }
              if (successfulComboFound == true)
              {
                    break;
              }
              output.str("0");

              for (i = 0; i < len && ++entry[i] == nbletters; i++)
                    entry[i] = 0;
        } while (i < len);

        if (successfulComboFound == true)
        {
              comboFoundGlobal = true;
              finishedThreads.push_back(true);
        }
        else
        {
              finishedThreads.push_back(true);
        }
  };

Threads created here : 在这里创建线程:

  thread *threadArray[numberOfThreads];

  for (int f = 0; f < numberOfThreads; f++)
  {
        threadArray[f] = new thread(threadLambda, f + 1, lettersPointer, operationsPointer, ((int)(workingBeginOperations[f])) - 48);
  }

If any more of the code is needed to help solve the problem, please let me know and I will edit the post to add the code. 如果需要更多代码来帮助解决问题,请告诉我,我将在帖子中添加代码。 Thanks in advance for all of your help. 预先感谢您的所有帮助。

Your lambda object captures its arguments by reference [&] , so each copy of the lambda used by a thread references the same shared objects, and so various threads race and clobber each other. 您的lambda对象通过引用[&]捕获其参数,因此线程使用的lambda的每个副本都引用相同的共享对象,因此各种线程相互竞争和破坏。

This is assuming things like movesToSolve and winCombo come from captures (it is not clear from the code, but it seems like it). 这是假设movesToSolvewinCombo类的东西都来自捕获(虽然代码不清楚,但看起来确实如此)。 winCombo is updated when a successful result is found, but another thread might immediately overwrite it right after. 找到成功结果后,将更新winCombo ,但是另一个线程可能会立即立即覆盖它。

So every thread is using the same data, data races abound. 因此,每个线程都使用相同的数据,数据争用不胜枚举。

You want to ensure that your lambda works only on two three types of data: 您想确保您的lambda仅适用于两种三种类型的数据:

  1. Private data 私人数据
  2. Shared, constant data 共享的恒定数据
  3. Properly synchronized mutable shared data 正确同步的可变共享数据

Generally you want to have almost everything in category 1 and 2, with as little as possible in category 3. 通常,您希望类别1和2中的几乎所有内容,而类别3中的内容尽可能少。

Category 1 is the easiest, since you can use eg, local variables within the lambda function, or captured-by-value variables if you ensure a different lambda instance is passed to each thread. 类别1是最简单的,因为您可以在lambda函数中使用局部变量,或者如果确保将不同的lambda实例传递给每个线程,则可以使用按值捕获的变量。

For category 2, you can use const to ensure the relevant data isn't modified. 对于类别2,您可以使用const来确保不修改相关数据。

Finally you may need some shared global state, eg, to indicate that a value is found. 最后,您可能需要一些共享的全局状态,例如,表明已找到一个值。 One option would be something like a single std::atomic<Result *> where when any thread finds a result, they create a new Result object and atomically compare-and-swap it into the globally visible result pointer. 一个选项将类似于单个std::atomic<Result *> ,其中当任何线程找到结果时,它们将创建一个新的Result对象,并对其进行原子比较并将其交换为全局可见的结果指针。 Other threads check this pointer for null in their run loop to see if they should bail out early (I assume that's what you want: for all threads to finish if any thread finds a result). 其他线程在其运行循环中检查此指针是否为null,以查看是否应尽早纾困(我想这就是您想要的:如果任何线程找到结果,所有线程都将完成)。

A more idiomatic way would be to use std::promise . 一种更惯用的方法是使用std::promise

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM