1

据我了解,在对象内部启动线程时调用的函数不应该是类成员。最好的方法似乎是启动一个朋友功能,它可以让您重新访问您的对象。

通常,启动子线程的成员函数(以及父线程)可以继续,也可以返回。在我使用这种技术的每一种情况下,我都让启动器方法返回到调用它的父线程中的应用程序;类似于 Qt 线程。

当子线程完成它的工作时,它所做的最后一件事是返回到友元函数,该函数本身返回到等待返回的东西(pthread_koin 或 WaitForSingleEvent),或者,如果没有捕手,我猜你会说归于无处。

所以,这就是问题所在。如果友元函数的返回没有捕获器,即父线程不在成员函数中,我可以安全地销毁从友元函数启动子线程的对象吗?

编辑 - - - - - - - - - - - - - - - - - - - - - - - - - -------------------------

从回复中可以看出,我需要一个例子。我们会选择Windows。与Linux没有什么不同。我遗漏了很多东西,类定义等。

  1. Main 在堆上创建了一个 SomeObject。
  2. Main 调用 so->run() 并开始做其他事情。
  3. Run() 启动运行 SomeFriend() 的子线程。
  4. SomeFriend() 调用 so->Worker() (that == so)
  5. Worker() 执行任何操作并返回到 SomeFriend()。
  6. 我可以在这里删除吗?即delete that <<<=== 这个问题的主题。
  7. SomeFriend() 返回终止子线程。
//=================================================================
int main( int argc, char** argv )
{
   SomeObject* so = new SomeObject();
   so->run();
   while(1)
   {
      DoOtherTasks();  // but don't exit!
   }
   return 0;        

//=================================================================
void SomeObject::run();
(
  volatile DWORD      ThreadId;         // Thread ID
  HANDLE              threadHandle;

  try
  {
     threadHandle = CreateThread(
         NULL,                              // default security attributes
         0,                                 // set stack size: default = 0
         (LPTHREAD_START_ROUTINE)(SomeFriend),
         (LPVOID*)this,                     // func args: this
         0,                                 // default creation flags
         (LPDWORD)(&ThreadId)               // ptr to thread identifier
         );
  }
  catch ( ... )
     { throw; }
}   // launches the thread and returns.

//=================================================================
void*    SomeFriend( void* thisPtr )  // is a friend of SomeObject
{
   SomeObject*     that ((SomeObject*)thisPtr);

   that->Worker();

   // HERE IS WHERE THE QUESTION IS TALKING ABOUT
   // CAN I DO THIS SAFELY?
   delete that;

   return (void*)NULL;
}

//=================================================================
void SomeObject::Worker()  // remember, this is run in the daughter thread.
{
   // whatever
   return (void*)NULL;
}
4

3 回答 3

1

要回答您编辑的问题,是的,您可以delete that;但是,请记住,由于线程调度程序可能已经调度线程的方式,它调用的任何函数在调用的逻辑中的任何main()时候都可能无效。so so->run()

so在您调用so->run(). 如果没有受保护的逻辑main(),它的堆栈后代不应该再次接触。so

于 2013-04-04T02:32:27.690 回答
0

是的。

您的内存管理代码应该已经是线程安全的(或者线程开始时会很危险!)所以 free() 本身应该没问题。破坏也很好,只要你记住没有其他人可以引用这个对象,因为他们将指向一个被破坏的对象。

人们说它不应该是类成员的原因是成员函数有一个典型的隐藏指针,它在字节级别上与其他参数的处理方式也不同,所以你不能把它作为一个带有额外参数的普通函数来调用. 这使得它通常与具有所需特定调用约定的 pthread_create 和 CreateThreadEx 函数不兼容。这就是为什么你有一个保镖静态/全局/朋友函数来为你做这个调用约定转换(并且可能如此透明以至于你自己没有注意到它)。

于 2013-04-03T20:59:34.817 回答
0

没有内在的理由不将成员函数作为线程中的顶级函数启动。C++11 处理得很好:

struct S {
    void f();
};

S s;

int main() {
    std::thread thr(&S::f, s);
    thr.join();
    return 0;
}
于 2013-04-03T21:45:21.367 回答