【问题标题】:Extending scope of local variables in C over function calls通过函数调用扩展 C 中局部变量的范围
【发布时间】:2014-01-08 10:07:36
【问题描述】:

我有一个为用户提供函数调用的库,如下所示:

int* g_ID = NULL;

void processing(int p1, char p2)
{
  int ID = newID();

  g_ID = &ID;

  callback(p1, p2);

  return ID;
}

void SendResponse()
{
  sendID(*g_ID);
}

用户通过使用签名void (f*)(int p1, char p2) 注册其回调函数来设置其应用程序,并且不应该知道库内部使用的 ID。所以用户空间代码看起来像:

main()
{
  RegisterCallback(HandleRequest);

  while (inProgress())
    sleep(1); /* just sleep here */
}

void (HandleRequest*)(int val1, char val2)
{
  /* ... do something user specific ... */

  SendResponse();

  return;
}

这里的问题是,库(处理 ID 和 g_ID 不是线程安全的)!!用户的回调由其他库函数作为线程异步调用。多个线程可以以这种方式并行执行。但我不会让用户查看图书馆内部 ID。

我知道上面的代码 sn-ps 并不完美。这只是为了证明我的意图...SendResponse() 尚未实现;-)。

我希望,有人可以提供一些想法来“实现”SendResponse() 并保持线程安全。

【问题讨论】:

  • 增加局部变量生命周期的唯一方法是使其成为static。但是,这样做的缺点是,函数的所有调用都将具有完全相同的变量,每个函数调用都不会是“本地”的。
  • 嗯,首先,使用指向局部变量的指针是不好的——它肯定会在调用返回后不久被覆盖。您应该改用malloc 或等效项,或@Joachim 建议的static 关键字。至于线程安全,如果你能够使用 C11,你可以使用新的 thread_local 关键字。
  • 我忘了说,动态内存分配是不允许的:-/ 一切都必须在编译时“分配”。
  • static thread_local 应该可以工作(再次假设您可以使用 C11)。
  • 好吧,不幸的是,thread_local 不可用,因为我使用的是 gcc 4.6,而 C11 首先是 gcc 4.8.1 正式可用的(我从其他线程得到的;甚至不确定 C11到目前为止是官方的,似乎只有 C++11)。

标签: c windows multithreading


【解决方案1】:

您可以在此处使用 threadlocal 来保留 g_ID,而不是使用全局。据我了解,这将适用于以下场景,即可能有来自不同线程的多个并发调用 process(),但 process() 方法如图所示 - SendResponse() 调用只会在范围内发生callback() 方法的(运行时范围,非词法)。在显示的代码中确实如此。如果 HandleRequest 做了一些奇特的事情,比如启动另一个线程然后返回(但你当然可以通过文档禁止它),这可能是不真实的。

另一种更经典的方法是将您关心的所有状态(如 g_ID)封装到 void *opaque_state * 或其他任何内容中,然后传递给回调,然后将方法如 SendRespose()以此为论据。如果您不喜欢void *,您可以实现opaque_state * 版本,而无需使用前向声明公开该结构的任何细节。

【讨论】:

  • 我没有在第一段中得到你的意图......你能举一些小例子吗?对于第二点:重点是,所有用户回调都有一个共同的签名并且已经设置好了。添加一些变量意味着更改所有用户功能,这是我想避免的,但我将来可能会想到......
  • 基本上将 process() 的声明更新为: __threadlocal int* g_ID = NULL;然后,process() 调用中的每个 g_ID 分配将独立于任何其他对进程的调用(因为 g_ID 不再是全局的,而是线程本地的)。如果我能很好地理解您的要求,这一切都会奏效。你能清楚地识别出库代码的代码吗?第一个样本?谁打电话给process()callback() 定义在哪里?
  • 第一段代码是库部分,processing()是IPC消息接收调用的线程函数,SendResponse()应由用户在其函数回调之外使用,这里HandleRequest .因此,第二部分是用户代码。 __threadlocal 是什么?如果我将其设置为 processing() 的本地地址,SendResponse() 如何知道 g_ID
  • 抱歉,我的意思是__thread - 例如,请参阅here(其他平台也有)。您不会将 g_ID 设为本地处理。和以前一样,您将其保持为静态,但使用 __thread 声明它,这意味着每个线程都有一个私有值,因此从那里调用的每个 callback() 和方法基本上都会看到自己的 g_ID 版本。如果您查看线程局部变量的工作原理,这将更有意义。
  • 好的,我会尽快尝试__thread,以便再次访问我所有的平台开发 IDE。我不知道__thread,但这听起来很有希望。谢谢。
猜你喜欢
  • 1970-01-01
  • 2020-02-13
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
  • 2013-02-15
  • 2011-03-03
  • 1970-01-01
  • 2013-10-14
相关资源
最近更新 更多