局部静态变量如何在c中不安全?

时间:2018-06-11 10:50:10

标签: c multithreading static thread-safety

所以环顾互联网,我无法找到有关此问题的一致且有用的信息。所以这就是问题所在:

为什么C中的局部静态变量被称为线程不安全?我的意思是,静态局部变量存储在数据段中,由所有线程共享,但不是内部链接应该阻止线程插入彼此的静态变量?

This论坛帖子似乎暗示线程确实偶尔会在彼此的数据段中进行,但是这样的行为自90年代以来不会明显违反所有c标准吗?如果预期会出现这样的行为,那么很久以前在连续的c标准中是否会使用数据段(即所有具有静态存储持续时间的变量,包括全局变量)?

我真的不明白这一点,因为每个人似乎都有一些针对局部静态变量的东西,但是人们似乎无法就其原因达成一致,并且研究一些论证表明它们是错误的。

我认为,本地静态变量是在函数调用之间传递信息的一种非常好的方法,它可以真正提高可读性并限制范围(比如,将信息作为参数传递并在每个参数上写回来)函数调用)。

据我所知,本地静态变量有完全合法的用法。但也许我错过了什么?我真的很想知道是不是这样。

[编辑]:这里的答案非常有帮助。感谢大家的洞察力。

5 个答案:

答案 0 :(得分:3)

  

但不是内部链接应该阻止线程踩到彼此的静态变量?

不,链接与线程安全无关。它只是限制函数访问在其他作用域中声明的变量,这是一个不同且无关的问题。

让我们假设您有这样的功能:

int do_stuff (void)
{
  static int x=0;
  ...
  return x++;
}

然后这个函数由多个线程,线程1和线程2调用。线程回调函数不能直接访问x,因为它具有本地范围。但是,他们可以拨打do_stuff(),他们可以同时拨打电话。然后你会得到这样的场景:

  • 线程1已执行do_stuff,直到该点返回0来调用。
  • 线程1即将向1写入值x,但在此之前......:
  • 上下文切换,线程2进入并执行do_stuff
  • 线程2读取x,它仍为0,因此它会向调用者返回0,然后x增加1
  • x现在是1
  • 线程1再次获得焦点。它即将1存储到x,这就是它的作用。
  • 现在x仍为1,但如果程序运行正常,则应为2

当在多个指令中完成对x的访问时,情况变得更糟,因此一个线程读取“x的一半”,然后被中断。

这是一种“竞争条件”,这里的解决方案是使用互斥或​​类似的保护机制来保护x。这样做会使函数线程安全。或者,可以重写do_stuff以不使用任何静态存储变量或类似资源 - 然后它将重入

答案 1 :(得分:1)

  

是不是内部联系应该阻止线程互相踩踏静态变量?

Linkage与并发性无关:内部链接阻止翻译单元,而不是线程,看不到彼此的变量。

  

我认为,本地静态变量是在函数调用之间传递信息的一种非常好的方法,可以真正提高可读性并限制范围

通过静态变量在调用之间传递信息与通过全局变量传递信息没有什么不同,原因相同:当你这样做时,你的函数变得不可重入,严重限制了它的用途。

问题的根本原因是具有静态链接的变量的读/写使用将函数形式从无状态转换为有状态。如果没有静态变量,函数控制的任何状态都必须从外部传递给它;另一方面,静态变量让函数保持"隐藏"状态。

要了解保持隐藏状态的后果,请考虑strtok函数:您不能同时使用它,因为多个线程会互相踩到彼此的状态。此外,如果您希望从当前正在解析的字符串中解析每个标记,则即使从单个线程也无法使用它,因为您的第二级调用会干扰您自己的顶级调用。

答案 2 :(得分:1)

从我的观点来看,基础是错误的,或者至少,它与其他任何糟糕的设计一样不安全。

糟糕的软件实践(或线程不安全)可能是共享资源而没有标准或保护类型(线程之间的通信有不同且很好的方式,例如队列,邮箱等,或者信号量和互斥量,如果资源有要分享),但这是开发人员的错,因为他们没有使用正确的机制。

实际上我看不出你的观点,一个静态局部变量,其范围定义得很好(甚至更好,对于嵌入式应用程序有助于避免内存溢出)并且无法访问它,所以我猜没有关系不安全代码和静态局部变量之间(或至少,不是一般意义)。

如果你正在谈论一个静态局部变量,它可以从两个不同的任务中写入/读取/没有保护(通过回调或其他),这是一个可怕的设计(再次,开发人员的错),但不,因为静态局部变量(通常)不安全。

答案 3 :(得分:0)

同时读取和写入任何非原子对象的行为在C中是 undefined

static变量使这种情况发生的可能性大大超过自动变量或动态变量。这就是问题的症结所在。

因此,如果您不控制线程(例如使用互斥单元),您可以将程序置于未定义状态。

一种中途宿舍;某些C编译器可以使用线程本地存储,但它尚未被纳入C标准(参见C ++ 11的thread_local)。例如,请参阅https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Thread-Local.html

答案 4 :(得分:0)

  

是不是内部链接应该阻止线程踩到彼此的静态变量?

您的问题已标记为。 C编程语言中没有线程。如果您的程序创建了任何新线程,它会通过在运行时调用某个库来实现。 C工具链不知道什么是线程,它无法知道你调用的库例程创建线程,并且它无法知道你是否认为任何特定的静态变量被一个或另一个线程“拥有”线程。

程序中的每个线程都在与每个其他线程相同的虚拟地址空间中运行。每个线程可能都可以访问任何其他线程可以访问的所有相同变量。如果程序中的变量实际 被多个线程使用,那么程序员的责任(而不是工具链的责任)就是确保线程在一个线程中使用它安全的方式。

  

每个人似乎都有一些针对本地静态变量的东西,

团队合作开发大型,长期存在的软件系统的软件开发人员(想想,数十年,数十万到数千万行代码)有一些很好理解的原因可以避免使用静态变量。不是每个人都在这样的系统上工作,但是你会遇到一些人。

  

人们似乎无法就其原因达成一致

并非所有软件系统都需要维护和升级数十年,而且并非所有系统都有数千万行代码。这是一个大世界。出于各种原因,有些人在那里编写代码。他们并不都有相同的需求。

  

并且研究一些论点表明它们是错误的构想

有些人出于许多不同的原因编写代码......对你来说似乎“不合理”的东西可能是其他一些开发人员长期以来一直在思考的问题。也许您并不完全理解他们的需求。

  

据我所知,本地静态变量完全合法使用

是。这就是他们存在的原因。与许多其他编程语言一样,C编程语言是一种可以以多种不同方式使用的通用工具。