在另一个线程的堆栈上访问和修改自动变量

时间:2011-08-25 05:19:32

标签: c++ multithreading

我想在线程周围传递一些数据,但是如果我可以管理它,我想避免使用全局变量。我编写线程例程的方式是用户为线程生命周期的每个“阶段”传递一个单独的函数:例如,这将是产生线程的典型用法:

void init_thread(void *arg) {
  graphics_init(); 
}
void process_msg_thread(message *msg, void *arg) {
  if (msg->ID == MESSAGE_DRAW) {
    graphics_draw();
  }
}
void cleanup_thread(void *arg) {
  graphics_cleanup();
}

int main () {
  threadCreator factory;
  factory.createThread(init_thread, 0, process_msg_thread, 0, cleanup_thread, 0);
  // even indexed arguments are the args to be passed into their respective functions
  // this is why each of those functions must have a fixed function signature is so they can be passed in this way to the factory
}

// Behind the scenes: in the newly spawned thread, the first argument given to 
// createThread() is called, then a message pumping loop which will call the third 
// argument is entered. Upon receiving a special exit message via another function 
// of threadCreator, the fifth argument is called.

最直接的方法是使用全局变量。我想避免这样做,因为它是糟糕的编程实践,因为它会产生混乱。

当我尝试稍微改进我的例子时出现了一个问题:

void init_thread(void *arg) {
  GLuint tex_handle[50]; // suppose I've got 50 textures to deal with.
  graphics_init(&tex_handle); // fill up the array with them during graphics init which loads my textures
}
void process_msg_thread(message *msg, void *arg) {
  if (msg->ID == MESSAGE_DRAW) { // this message indicates which texture my thread was told to draw
    graphics_draw_this_texture(tex_handle[msg->texturehandleindex]); // send back the handle so it knows what to draw
  }
}
void cleanup_thread(void *arg) {
  graphics_cleanup();
}

我在这里大大简化了与图形系统的交互,但你明白了。在此示例中,代码tex_handle是一个自动变量,当init_thread完成时,其所有值都将丢失,因此在process_msg_thread需要引用它时将无法使用。

我可以通过使用全局变量来解决这个问题,但这意味着我不能同时拥有(例如)其中两个线程,因为它们会相互踩踏纹理句柄列表,因为它们使用相同的线程。

我可以使用线程局部全局变量,但这是一个好主意吗?

我想出了最后一个想法。我可以在父线程中的堆上分配存储空间,并将指针发送给子节点以便进行处理。所以我可以在父线程离开时释放它,因为我打算在它退出之前清理它的子线程。所以,像这样:

void init_thread(void *arg) {
  GLuint *tex_handle = (GLuint*)arg; // my storage space passed as arg
  graphics_init(tex_handle); 
}
void process_msg_thread(message *msg, void *arg) {
  GLuint *tex_handle = (GLuint*)arg; // same thing here
  if (msg->ID == MESSAGE_DRAW) { 
    graphics_draw_this_texture(tex_handle[msg->texturehandleindex]); 
  }
}
int main () {
  threadCreator factory;
  GLuint *tex_handle = new GLuint[50]; 
  factory.createThread(init_thread, tex_handle, process_msg_thread, tex_handle, cleanup_thread, 0);
  // do stuff, wait etc
  ...
  delete[] tex_handle;
}

这看起来或多或少是安全的,因为我的值在堆上,我的主线程分配它然后让孩子们按照他们的意愿搞乱它。孩子们可以自由使用存储空间,因为指针被赋予了所有需要访问的功能。

所以这让我想到为什么不让它成为一个自动变量:

int main () {
  threadCreator factory;
  GLuint tex_handle[50]; 
  factory.createThread(init_thread, &tex_handle, process_msg_thread, &tex_handle, cleanup_thread, 0);
  // do stuff, wait etc
  ...
} // tex_handle automatically cleaned up at this point

这意味着子线程直接访问父堆栈。我想知道这是否是犹太人。 我在互联网上找到了这个:http://software.intel.com/sites/products/documentation/hpc/inspectorxe/en-us/win/ug_docs/olh/common/Problem_Type__Potential_Privacy_Infringement.htm

似乎Intel Inspector XE检测到此行为。也许我不应该这样做?这仅仅是对URL所暗示的潜在隐私侵权的警告,还是存在我可能不知道的其他潜在问题?

P.S。在仔细思考了所有这些之后,我意识到这种将线程分成一堆独立调用的函数的架构也许并不是一个好主意。我的目的是消除为每个生成的线程编写消息处理循环编码的复杂性。我曾经预料到可能出现的问题,如果我有一个通用的线程实现,总是检查消息(比如我的自定义线程指定线程将被终止),那么我可以保证一些未来的用户不会意外忘记检查该条件在他们的每个消息循环中。

我的解决方案存在的问题是,这些单独的功能现在是分开的,无法相互通信。它们可能只通过全局变量和线程局部全局变量来实现。我猜线程本地全局变量可能是我最好的选择。

P.P.S。这让我想到了RAII以及线程的概念,至少我最终表示它与资源的概念有一定的相似性。也许我可以构建一个比传统方式更自然地表示线程的对象......不知何故。我想我会睡上去。

1 个答案:

答案 0 :(得分:2)

将您的线程函数放入类中。然后他们可以使用实例变量进行通信这需要更改线程工厂,但这是解决问题的最简单方法。

只要您可以保证堆栈帧包含数据的函数在子线程退出之前永远不会返回,那么使用自动变量的想法也会起作用。即使在main()返回之后,子线程仍然可以运行,这实际上并不容易实现。