【问题标题】:C++ 11 thread and pointer parameterC++ 11 线程和指针参数
【发布时间】:2015-04-03 14:19:15
【问题描述】:

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

操作系统版本:CentOS Linux release 7.0.1406 (Core)

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 = %s\n", sz);
}

void myFunc2(const char* const sz)
{
    printf("const char* const sz = %s\n", sz);
}

void myFunc3(string str)
{
    printf("string str = %s\n", 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 second MyFunc1 可能会输出空值,例如 sz =,为什么?

非常感谢。

【问题讨论】:

  • 您在线程有时间读取它之前覆盖了 sz。
  • szstr 都是在循环的下一次迭代之前退出范围的自动变量。从 t1t2 访问 either 会在它们被销毁后调用未定义的行为。因此,您的程序格式错误。 t3 有效,因为您正在向线程发送str 的副本。
  • @Baldrick,如果 MyFunc1 是来自第 3 方的 API,如何解决。
  • 仅供参考std::this_thead::sleep_for(std::chrono::seconds(1));
  • 所以编写一个调用他们的 API 但拥有资源的包装器,并将该包装器用作线程过程。一旦他们的 API 完成,您的包装器可以在完成线程之前进行清理。谁说你必须将他们的 API 函数直接传递给std::thread?你可以传递一个 you 编写的包装器,并从 that 调用他们的包装器。但如果可能的话,我强烈建议加入。

标签: c++ multithreading pointers c++11


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

    我的答案是:

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

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

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

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

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

    我建议您阅读有关 thread::join() 的内容,甚至一般性地阅读有关编写线程应用程序的内容。

    【讨论】:

      猜你喜欢
      • 2013-10-10
      • 2017-06-29
      • 1970-01-01
      • 2015-02-25
      • 2016-09-07
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多