【发布时间】:2019-07-23 02:44:12
【问题描述】:
我一直在开发一个程序,该程序基本上使用蛮力向后工作,以找到一种使用给定操作集达到给定数量的方法。因此,例如,如果我给出了一组操作 +5、-7、*10、/3,并且给定的数字说 100(*这个例子可能不会想出一个解决方案),还有一个给定的要解决的最大移动量(比如说 8),它将尝试使用这些操作来达到 100。这部分使用我在应用程序中测试过的单个线程工作。
但是,我希望它更快,所以我开始使用多线程。我已经工作了很长时间,甚至让 lambda 函数工作,经过一些认真的调试后,我意识到解决方案“组合”在技术上是找到的。然而,在它被测试之前,它被改变了。考虑到我认为每个线程都有自己的 lambda 函数副本及其要使用的变量,我不确定这怎么可能。
总之,程序从解析信息开始,然后将解析器划分的信息作为参数传递到操作对象(有点像函子)的数组中。然后它使用一种算法生成组合,然后由操作对象执行。简单来说,该算法接受操作量,将其分配给一个 char 值(每个 char 值对应一个操作),然后输出一个 char 值。它生成所有可能的组合。
这是我的程序如何工作的总结。除了两件事之外,一切似乎都运行良好且井然有序。还有一个错误我没有添加到标题中,因为有一种方法可以修复它,但我对替代方案很好奇。这种方式也可能对我的电脑不好。
所以,回到与线程一起输入的 lambda 表达式的问题,正如我在调试器中使用断点所看到的那样。似乎两个线程都没有生成单独的连击,而是更正确地在第一个数字之间切换,而是交替连击。因此,它将转到 1111、2211,而不是生成 1111、2111。(这些生成如上一段所示,但它们一次完成一个字符,使用字符串流组合),但一旦它们退出循环将连击填满,连击就会丢失。它会在两者之间随机切换,并且永远不会测试正确的组合,因为组合似乎是随机打乱的。我意识到这一定与竞争条件和互斥有关。我原以为我已经通过不更改从 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(functions) 是将函数的数量存储在元组中的位置,并且是要存储在数组中的对象的数量。 paramterStrings 是一个向量,其中存储了用作类的构造函数的参数的字符串。此代码导致“异常跟踪/断点陷阱”。如果我改用“*操作”,我会在定义类的文件中出现分段错误,第一行显示“类操作”。另一种方法是注释掉删除部分,但考虑到它是使用“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);
}
如果需要更多代码来帮助解决问题,请告诉我,我将编辑帖子以添加代码。提前感谢您的所有帮助。
【问题讨论】:
-
虽然你已经包含了你的代码很好,但你最好创建一个 minimal 示例,它说明了你在使用 lambda 时遇到的问题,而不需要一个巨大的介绍和不仅仅是一屏代码。
-
我会尝试,但老实说,我不太确定 Lambda 表达式等需要哪些代码,因为这是我第一次在多线程中使用 Lambda 表达式一个程序。另外,我知道介绍很大,我将尝试对其进行删节。谢谢:)
-
好吧,你展示的代码太多也太少了。从某种意义上说,我们看不到 lambda 捕获的内容以及
len、movesToSolve或comboFoundGlobal等内容的来源。 -
comboFoundGlobal 只是在 int main() 的开头初始化为 false boolean,len 就是 move - 1,moves 是开头输入的移动数量,movesToSolve 也就是在程序开始时初始化为 0 的 int。
-
这些只是示例,您的散文描述不一定有帮助,因为现在我需要知道它们是否是全局变量等。这就是您需要 MCVE 的原因。
标签: c++ multithreading lambda stdthread