c++ - 使用lambda表达式创建线程时,如何为每个线程提供自己的lambda表达式拷贝?

标签 c++ multithreading lambda stdthread

我一直在研究一个程序,该程序基本上使用蛮力向后工作,以找到一种使用给定操作集达到给定数量的方法。因此,例如,如果我给出了一组运算+ 5,-7,* 10,/ 3,并且给定的数字为100(*此示例可能不会给出解决方案),要解决的最大移动量(比如说8),它将尝试使用这些操作达到100。这部分使用我在应用程序中测试过的单个线程工作。

但是,我希望它更快,因此我开始使用多线程。我花了很长时间才能使lambda函数正常工作,并且经过认真的调试后才意识到,从技术上来说,找到了“ combo”解决方案。但是,在测试之前,它已被更改。考虑到我认为每个线程都拥有自己的lambda函数副本和要使用的变量,因此我不确定这是如何实现的。

总而言之,程序首先通过解析信息开始,然后将由解析器划分为信息的信息传递到操作对象(有点像函子)的数组中。然后使用算法生成组合,然后由操作对象执行组合。为简单起见,该算法吸收大量运算,将其分配给一个char值(每个char值对应于一个运算),然后输出一个char值。它生成所有可能的组合。

那是我的程序如何工作的总结。除了两件事之外,其他一切似乎都正常运行。我没有添加到标题中,这是另一个错误,因为有一种解决方法,但是我对替代方法感到好奇。这种方式也可能对我的计算机不利。

因此,返回到通过线程输入的lambda表达式的问题,正如我在调试器中使用断点所看到的那样。似乎两个线程都没有生成单独的连击,而是更恰当地在第一个数字之间切换,而是交替切换。因此,它将是1111、2211,而不是生成1111、2111(它们是如上一段所示生成的,但是它们一次完成一个char并使用stringstream组合),但是一旦它们退出循环填满了组合,组合就会迷路。它会在两者之间随机切换,并且永远不会测试正确的组合,因为组合似乎会被随机打乱。我意识到这一定与种族状况和相互排斥有关。我以为我通过不更改从lambda表达式外部更改的任何变量来避免了这一切,但是似乎两个线程都使用相同的lambda表达式。

我想知道为什么会发生这种情况,以及如何做到这一点,以便可以说创建这些表达式的数组并为每个线程分配自己的线程,或者类似于避免整体处理互斥的内容。

现在,当我最后删除我的操作对象数组时,会发生另一个问题。分配它们的代码和删除代码如下所示。

  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;


get <0>(functions)是将函数的数量存储在元组中的位置,并且是要存储在数组中的对象的数量。 paramterStrings是一个向量,其中存储了用作类的构造函数的参数的字符串。此代码导致“异常跟踪/断点陷阱”。如果我改用“ * operations”,则会在定义类的文件中出现段错误,该行的第一行显示“类操作”。另一种选择是只注释掉删除部分,但是考虑到使用“ new”运算符创建它并可能导致内存泄漏的事实,我很确定这样做是一个坏主意。

以下是lambda表达式的代码,以及用于创建线程的相应代码。我在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);
        }
  };


在这里创建线程:

  thread *threadArray[numberOfThreads];

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


如果需要更多代码来帮助解决问题,请告诉我,我将在帖子中添加代码。预先感谢您的所有帮助。

最佳答案

您的lambda对象通过引用[&]捕获其参数,因此线程使用的lambda的每个副本都引用相同的共享对象,因此各种线程相互竞争和破坏。

这是假设movesToSolvewinCombo之类的东西来自捕获(虽然代码不清楚,但看起来很像)。找到成功结果后,将更新winCombo,但是另一个线程可能会立即立即覆盖它。

因此,每个线程都使用相同的数据,数据争用不胜枚举。

您想确保您的lambda仅适用于两种三种类型的数据:


私人数据
共享的恒定数据
正确同步的可变共享数据


通常,您希望类别1和2中几乎所有内容,而类别3中的内容尽可能少。

类别1是最简单的类别,因为您可以在lambda函数中使用局部变量,或者如果确保将不同的lambda实例传递给每个线程,则可以使用按值捕获的变量。

对于类别2,可以使用const来确保不修改相关数据。

最后,您可能需要一些共享的全局状态,例如,表明已找到一个值。一种选择是类似于单个std::atomic<Result *>,其中当任何线程找到结果时,它们都会创建一个新的Result对象,并将其原子地进行比较并交换为全局可见的结果指针。其他线程在其运行循环中检查此指针是否为null,以查看是否应尽早解救(我想这就是您想要的:如果任何线程找到结果,所有线程都将完成)。

一种更惯用的方式是使用std::promise

关于c++ - 使用lambda表达式创建线程时,如何为每个线程提供自己的lambda表达式拷贝?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57155980/

相关文章:

c - C中的主线程在一定时间后终止

java - 将 Map<String, List<String>> 转换为 Map<String, Object[]>

java - 如何在 Kotlin 中使用现有的比较器

c++ - 将字符串添加到 CEdit 文本框而不是 CCombobox

c++ - 在 meta-toolkit 的 config.toml 中配置默认​​链

java - synchronized vs ReentrantLock 无竞争锁

python - Lambda 函数要求输入两次,

c++ - 具有私有(private)构造函数的类的全局对象

c++ - 传递 std::ref(value) 时是否应应用 std::optional 的推导指南?

c++ - 将数据从一个线程传递到另一个线程的最快方法