【问题标题】:std::thread context of execution (c++14)std::thread 执行上下文 (c++14)
【发布时间】:2023-03-08 20:30:01
【问题描述】:

当 std::thread 调用的函数的 in/out 变量在执行期间更改值时出现问题...


功能:

static int func(stThread_t *&pStThread)

参数

pStThread:它是一个结构体,有一个指向 std::thread 和其他变量(一些标志)的指针

typedef struct stThread {
   stThread() noexcept {...};    
   stThread(const stThread &cRigth) noexcept {...};    
   stThread & operator = (const stThread &cRigth) noexcept {...};

   std::thread *pThread;
   volatile bool bBegin;
   volatile bool bEnd;

 } stThread_t;

函数func打印参数的std::thread的地址 pStThread 和线程 id

func 之前 1785280 this_id 21968

创建 this_thread::sleep 2 秒后,再次打印

func afer ... this_id 21968

   static int func(stThread_t *&pStThread) {

      std::thread::id this_id = std::this_thread::get_id();

      long long p_begin = (long long)pStThread;
      std::cout << "func before " << std::to_string(p_begin) << " this_id " << this_id << "\n";
      std::cout.flush();

      pStThread->bBegin = true;

      std::this_thread::sleep_for(std::chrono::milliseconds(2000));

      this_id = std::this_thread::get_id();
      long long p_end = (long long)pStThread;
      std::cout << "func afer " << std::to_string(p_end) << " this_id " << this_id << "\n";
      std::cout.flush();

      pStThread->bEnd = true;

      return 1;
   };

指向 std::thread 的指针的地址发生了变化(损坏、删除……?)


pStThread 是指针struct stThread_t 列表的push_back

std::list<stThread_t*> listOfThreads;
listOfThreads.push_back(pStThread);

我阅读了有关 std::move 的信息,但不适用于指针


最后有一个线程“垃圾收集器”试图清除所有等待执行的线程。


这里有完整的代码

#include <string>
#include <list>
#include <vector>
#include <map>
#include <thread>
#include <mutex>
#include <atomic>

#include <iostream>

typedef struct stThread {
   stThread() noexcept {
      pThread = NULL;
      bBegin = false;
      bEnd = false;
   };

   stThread(const stThread &cRigth) noexcept {
      this->pThread = cRigth.pThread;
      this->bBegin = (bool)cRigth.bBegin;
      this->bEnd = (bool)cRigth.bEnd;
   };

   stThread & operator = (const stThread &cRigth) noexcept {
      this->pThread = cRigth.pThread;
      this->bBegin = (bool)cRigth.bBegin;
      this->bEnd = (bool)cRigth.bEnd;

      return *this;
   };

   std::thread *pThread;
   volatile bool bBegin;
   volatile bool bEnd;

} stThread_t;

class CMain
{
public:
   typedef std::list<stThread_t*> MyList_threads;
   MyList_threads listOfThreads;

public:
   CMain() {

      std::cout << std::boolalpha << "Ex1 is move-constructible? "
         << std::is_move_constructible<stThread_t>::value << '\n'
         << "Ex1 is trivially move-constructible? "
         << std::is_trivially_move_constructible<stThread_t>::value << '\n'
         << "Ex1 is nothrow move-constructible? "
         << std::is_nothrow_move_constructible<stThread_t>::value << '\n'
         << "Ex2 is trivially move-constructible? "
         << std::is_trivially_move_constructible<stThread_t>::value << '\n'
         << "Ex2 is nothrow move-constructible? "
         << std::is_nothrow_move_constructible<stThread_t>::value << '\n';
   };

   static int func(stThread_t *&pStThread) {

      std::thread::id this_id = std::this_thread::get_id();

      long long p_begin = (long long)pStThread;
      std::cout << "func before " << std::to_string(p_begin) << " this_id " << this_id << "\n";
      std::cout.flush();

      pStThread->bBegin = true;

      std::this_thread::sleep_for(std::chrono::milliseconds(2000));

      this_id = std::this_thread::get_id();
      long long p_end = (long long)pStThread;
      std::cout << "func afer " << std::to_string(p_end) << " this_id " << this_id << "\n";
      std::cout.flush();

      pStThread->bEnd = true;

      return 1;
   };

   int _createThreads() {
      for (int iIdx = 0; (iIdx < 5); iIdx++) {
         stThread_t *pStThread = new stThread_t;

         pStThread->pThread = new std::thread(&CMain::func,
            std::ref(pStThread));

         if (pStThread) {
            do {
               std::this_thread::sleep_for(std::chrono::milliseconds(100));
            } while (!pStThread->bBegin);
            listOfThreads.push_back(pStThread);

            std::string sLog;
            sLog = "\nlistOfThreads.push_back " + std::to_string((long long)pStThread) + "\n";
            std::cout << sLog;
            std::cout.flush();
         }

         std::this_thread::sleep_for(std::chrono::milliseconds(1));
      }
      return 1;
   };

   int _main() {

      _createThreads();

      std::thread thread_collector([=]() {
         bool bEnd = false;
         MyList_threads::iterator it;
         it = listOfThreads.end();

         do {
            stThread_t *pStThread = NULL;

            if (it == listOfThreads.end()) {
               it = listOfThreads.begin();
               if (it == listOfThreads.end()) bEnd = true;
            }
            else it++;

            if (it != listOfThreads.end()) {
               if ((*it)->bEnd) {
                  pStThread = *it;

                  listOfThreads.erase(it);
                  it = listOfThreads.begin();
               }
            }

            if (pStThread) {
               if (pStThread->pThread) {
                  if (pStThread->pThread->joinable()) {
                     pStThread->pThread->join();

                     std::cout << " element deleted  " << std::to_string((long long)pStThread) << "\n";
                     std::cout.flush();
                  }
                  delete pStThread->pThread;
                  pStThread->pThread = NULL;
               }
               delete pStThread;
            }
            pStThread = NULL;


            std::this_thread::sleep_for(std::chrono::milliseconds(1));

         } while (!bEnd);
      });



      if (thread_collector.joinable()) {
         thread_collector.join();
      }

      return 1;
   };
};

int main()
{
   CMain _main;
   _main._main();

   return 0;
}

【问题讨论】:

  • 如果您能缩短代码并指出有问题的区域,那就太好了。
  • volatile bool 不是线程安全的;你想要std::atomic_bool,又名std::atomic&lt;bool&gt;
  • 如果你使用 std::thread 你可以使用 ThreadSanitizer 和最近的 clang 和 gcc
  • 另一个小提示:_createThreads 不是用户在全局命名空间范围内使用的合法标识符;保留以下划线后跟小写字母开头的标识符。还有一些其他类别的保留标识符;安全的做法是使用单个 trailing 下划线,而不是 leading 下划线。这听起来像是迂腐的吹毛求疵,但有时您会遇到名称冲突,而且有时会导致极其晦涩的问题。

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


【解决方案1】:

您有一个相当简单的错误,它与线程几乎无关:

(1) func 获取stThread_t*引用

static int func(stThread_t *&pStThread);

(2) 你传入pStThread的引用

std::thread(&CMain::func,std::ref(pStThread));

(3) 这是一个局部变量,其生命周期在循环迭代完成后立即结束

  for (int iIdx = 0; (iIdx < 5); iIdx++) {
     stThread_t *pStThread = new stThread_t;
     //...
  }

(4) 因此,当您的函数在对象被销毁后尝试访问该对象时,您会得到未定义的行为。 (注意!这里的“对象”指的是指针,而不是指针指向的对象)

不清楚你为什么坚持通过引用传递pStThread;您的函数实际上并没有修改指针(只是所指向的内容),而且您似乎不打算做这种设备实际上适合的任何事情。

【讨论】:

  • (2) 如果我为此更改pStThread-&gt;pThread = new std::thread(&amp;CMain::func, //std::ref(pStThread) pStThread); ,编译器会说:1&gt;D:\Archivos de programa (x86)\Microsoft Visual Studio 14.0\VC\include\thr/xthread(238): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &amp;&amp;,_Types &amp;&amp;...)'
  • 更改static int func(stThread_t *pStThread) { 工作正常!!谢谢!
猜你喜欢
  • 2020-02-13
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
相关资源
最近更新 更多