C++ 11 线程和指针参数

C++ 11 thread and pointer parameter

本文关键字:指针 参数 线程 C++      更新时间:2023-10-16

我想使用 5 个线程打印内容为 1、2、3、4、5 的字符串, 我的工作环境:

操作系统版本:CentOS Linux 版本 7.0.1406 (核心)

g++ 版本: g++ (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)

编译cmd: g++ -o thread_sz -std=c++11 -pthread thread_sz.cpp

// file name: thread_sz.cpp
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>
using namespace std;
void myFunc1(char* sz)
{
    printf("sz = %sn", sz);
}
void myFunc2(const char* const sz)
{
    printf("const char* const sz = %sn", sz);
}
void myFunc3(string str)
{
    printf("string str = %sn", str.c_str());
}
int main(int argc, char* argv[])
{
    for(int i = 1; i <= 5; i++) {
        char sz[16];
        sprintf(sz, "%2d", i);
        const string str(sz);
        std::thread t1(myFunc1, sz);
        std::thread t2(myFunc2, (const char* const)str.c_str());
        std::thread t3(myFunc3, str);
        t1.detach();
        t2.detach();
        t3.detach();
    }
    usleep(1000000); // sleep 1 second
    return 0;
}

输出如下内容:

const char* const sz =  1
sz =  3
const char* const sz =  2
string str =  3
sz =  4
sz =  4
string str =  2
sz =  5
const char* const sz =  4
sz =  5
string str =  1
const char* const sz =  5
string str =  4
string str =  5
const char* const sz =  5

我运行该程序很多次,结果似乎:

string str output: 100% correct
sz output: ~10% correct
const char* const sz output: ~50% correct

我的问题是:

1,为什么MyFunc1和MyFunc2不是100%正确的,顺便说一句,它们有什么区别。如何解决它。

2,MyFunc3能否100%正常工作。

3,如果我注释掉这一行 usleep(1000000); // sleep 1 secondMyFunc1 可以输出空值,例如 sz =,为什么?

谢谢。

在每个循环循环中,您创建变量(字符数组和字符串对象),并将其传递给在新的独立线程中运行的myFuncX。经常会发生主线程中的循环进入下一个周期的情况,但其中一个线程在该循环中启动并运行 myFuncX 未完成其运行。

函数行为之间的差异取决于参数的类型。 myFunc1 等待 char* 输入,因此指向字符(数组)的指针。主循环在每个循环中创建一个新的字符数组,并将其指针传递给 myFunc1。但是这个数组是一个局部变量,所以它的内容只在这个循环中有效。如果主循环步骤到下一个循环,则先前传递给 myFunc1 的参数的内容是不确定的,因为它指向被销毁的局部变量所在的内存位置。这就是为什么该方法有时或经常写入错误值的原因。

同样的事情发生在myFunc2的情况下。不同之处在于 myFunc2 等待指向字符串对象的字符数组的 const char* 指针,但这个字符串对象也是一个局部变量,因此在每个循环结束时也会被销毁。

但是,在myFunc3的情况下,方法等待字符串对象而不是指针。当您将其传递给myFunc3时,将在方法中创建一个新对象,该对象的内容与在主循环中创建的字符串相同。但是这个对象是不同的对象,只是它的内容是相同的。因此,在主循环中创建的字符串发生的情况都是一样的,myFunc3 使用自己的本地字符串对象,因此打印将是完美的。

我的回答是:

  1. MyFunc1 和 MyFunc2 是正确的,你可能不知道它们到底在做什么。他们真正在做的是在有预定时间执行它们的时候读取给定指针下的内存 - 他们只是读取当时存在的任何内容。没有人向您保证,for循环的下一次迭代中的sprintf()将在上下文切换以执行运行此函数的当前迭代中的线程之前发生。

    1.1. MyFunc2 理论上使用字符串,但它使用与下次覆盖方式完全相同的物理字符串变量。在这种特殊情况下,你的运气多于理性,因为很可能字符串在循环结束时被销毁,并且在下一次迭代时创建一个新字符串,这涉及释放内存并再次重新分配它,至少在理论上 - 你很幸运,这个新创建的字符串得到了与以前完全相同的指向字符串新分配内存的指针。实际上,您正在传递一个悬空指针,幸运的是,它与新分配的数组具有相同的值(当您使用 str.c_str() 并执行除立即复制或处理以外的任何其他操作来代替返回的字符数组时,总是会发生这种情况)。

  2. MyFunc3 也无法正常工作,你只是运气多于理性。它显示正确的数字,因为它将其副本保存在std::string类型变量中 - 因此,与 1 和 2 相反,从sz读取发生在线程构造时,而不是在执行线程函数时。实际上,您的函数不会引用除其自身变量之外的任何变量。但是,仍然不能保证为上一次迭代计划的函数将在为下一次迭代计划该函数之前完成其作业。您可能不会在您的情况下观察到它,但理论上 MyFunc3 可能会打印不按迭代顺序排列的值(例如,上一次迭代的线程已分配给另一个系统轻量级线程而不是下一次迭代的线程就足够了,并且来自先前迭代的线程由于某种原因略有延迟 - 您可能有一个情况, 例如,在 3 之前打印 4)。

  3. 如果您不将程序置于睡眠状态,那么当函数堆栈已经释放并可能被某些内容覆盖时,您的函数可能会被触发main()。在您的情况下,可能会发生这些是零的情况。运气不好,你会有一长串随机二进制数据,直到程序到达未分配给程序段的地址。

    3.1. 让程序进入睡眠状态只会延迟问题,而不是解决问题。

我建议您阅读有关编写线程应用程序的信息,甚至是一般的thread::join()