【问题标题】:boost::thread not updating global variableboost::thread 不更新全局变量
【发布时间】:2020-05-23 23:49:40
【问题描述】:

我在外部软件中使用包装函数来启动一个新线程,该线程会更新一个全局变量,但这对主线程来说似乎是不可见的。我不能调用 join(),而不是阻塞主线程并使软件崩溃。 boost::async、boost::thread 和 boost::packaged_task 的行为方式都相同。

uint32 *Dval;

bool hosttask1()
{

        while(*Dval<10)
        {
            ++*Dval;
            PlugIn::gResultOut << " within thread global value: " << *Dval << std::endl;    
            Sleep(500);
        }


return false;
}



void SU_HostThread1(uint32 *value)
{

Dval = value; 
*Dval = 2;
PlugIn::gResultOut << " before thread: " << *value <<  " before thread global: " << *Dval << std::endl;

    auto myFuture = boost::async(boost::launch::async,&hosttask1);

    //boost::thread thread21 = boost::thread(&hosttask1);
    //boost::packaged_task<bool> pt(&hosttask1);
    //boost::thread thread21 = boost::thread(boost::move(pt)); 
}

当我调用函数时:

number a=0 
su_hostthread1(a)
sleep(2) //seconds
result(" function returned "+a+"  \n")

OUTPUT:

before thread value: 2 before thread global value: 2
 within thread global value: 3
 within thread global value: 4
 within thread global value: 5
 within thread global value: 6
 function returned 2  
 within thread global value: 7
 within thread global value: 8
 within thread global value: 9
 within thread global value: 10

有什么想法吗? 提前致谢!

【问题讨论】:

  • 这是Dval 上的教科书数据竞赛,行为未定义。
  • 您的示例代码包含错误,因此无法讨论。请更改它,使其提供minimal reproducible example。作为这里的新用户,也请带上tour并阅读How to Ask

标签: multithreading c++11 boost boost-thread


【解决方案1】:

以下代码正确地再现了我打算做的事情。主要是线程更新一个主线程正确观察到的全局变量。

#include "stdafx.h"
#include <iostream>

#include <boost/thread.hpp>
#include <boost/chrono.hpp>

unsigned long *dataR;

bool hosttask1()
{
    bool done = false;
    std::cout << "In thread global value: " << *dataR << "\n"; //*value11 <<  *dataL << 
    unsigned long cc = 0;
    boost::mutex m;

        while (!done)
        {
            m.lock();
            *dataR = cc;
            m.unlock();
            cc++;
            std::cout <<  "In thread loop global value: "<< *dataR << "\n";
            if (cc==5) done = true;
        }


return done;
}

void SU_HostThread1(unsigned long *value)
{
    dataR = value;
    std::cout << "Before thread value: " << *value << " Before thread global value: " << *dataR << "\n"; //*value11 <<  *dataL << 
    auto myFuture = boost::async(boost::launch::async, &hosttask1);
    return;
}

int main()
{
    unsigned long value =1;
    unsigned long *value11;
    value11 = &value;

    SU_HostThread1(value11);

    boost::this_thread::sleep(boost::posix_time::seconds(1));
    std::cout << "done with end value: " << *value11 << "\n";

    return 0;
}

输出:

Before thread value: 1 Before thread global value: 1
In thread global value: 1
In thread loop global value: 0
In thread loop global value: 1
In thread loop global value: 2
In thread loop global value: 3
In thread loop global value: 4
done with end value: 4

然而,当我将它完全复制到外部软件的 SDK 时,主线程并没有更新全局值。有什么想法吗? 谢谢

在外部软件中输出:

before thread value: 1 before thread global value: 1
In thread global value: 1
In thread loop global value: 0
In thread loop global value: 1
In thread loop global value: 2
In thread loop global value: 3
In thread loop global value: 4
done with end value: 1 

【讨论】:

  • 答案可能是 main() 函数得到了适当的优化。同样,它不知道您是多线程的。它“知道”您将值 1 分配给变量 value 您没有声明为 volatile 或 atomic,并且它内联了 SU_HostThread1,并且看到您没有修改指向的值。所以它只是懒得再读一遍。如果您查看汇编程序,它可能只是打印了一个常量 1。
【解决方案2】:

如果您在线程之间共享数据,则必须同步对该数据的访问。两种可能的方式是保护所述数据的互斥锁和原子操作。简单的原因是存在缓存和读/写重新排序(由 CPU 和编译器)。虽然这是一个复杂的话题,在这里的答案中没有什么可以解释的,但是那里有几本好书,还有一堆正确的代码。

【讨论】:

  • 由于 SU_hostthread() 函数参数必须是一个指针,我如何将它分配给一个线程安全的全局原子变量,以便在线程内部使用?例如首先将 std::atomic atomicvalue 定义为全局的。然后在 SU_hostthread() 函数中将其重新分配为 std::atomic_exchange(&atomicvalue, value)。 “atomicvalue”应该和“value”指向同一个地址,这样外部软件才能正确读取。
【解决方案3】:

这可能是因为编译器在优化代码时通常不会考虑多线程。如果看到你的代码重复检查一个值,它知道在单线程中该值不能改变,所以它只是省略了检查。

如果您将变量声明为volatile,那么它可能会生成效率较低的代码,但会更频繁地进行检查。

当然你也要明白,当一个值被写入的时候,有些情况下它可能不会一次全部写入,所以如果你不幸在它写到一半的时候读回了它,那么你取回一个垃圾值。解决方法是将其声明为 std::atomic (优化器自动将其视为易失性),然后将发出更复杂的代码以确保写入和读取不能相交(或不同的处理器原语可能是用于整数等小对象)

大多数变量不在线程之间共享,当它们共享时,程序员需要在设计期间识别这些变量并平衡优化与线程同步需求。

【讨论】:

  • 不,volatile 不是多线程同步的答案。它可能适用于很多地方,但它不是正确的工具,并且它的使用不能提供正确的工具(原子或互斥体)提供的保证。
  • @UlrichEckhardt 确实,但这是朝着正确答案迈出的一步,这就是我讨论它的原因。但是原子和适当的栅栏和互斥锁是唯一可行的前进方式。这是一个更长的讨论,欢迎您发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-14
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 2022-09-22
相关资源
最近更新 更多