【问题标题】:Are static const variables thread-safe?静态常量变量是线程安全的吗?
【发布时间】:2010-07-13 11:14:03
【问题描述】:

我想知道静态常量变量是否是线程安全的?

示例代码sn-p:

void foo(int n)
{
     static const char *a[] = {"foo","bar","egg","spam"};
     if( ... ) {
       ...
      }
}

【问题讨论】:

    标签: c multithreading


    【解决方案1】:

    任何从未修改过的变量,无论它是否显式声明为 const,本质上都是线程安全的。

    const 不是编译器保证变量不可变的保证。 const 向编译器作出的承诺,即永远不会修改变量。如果你违背这个承诺,编译器会生成一个错误提示你,但你总是可以通过抛弃 constness 来使编译器静音。

    【讨论】:

      【解决方案2】:

      为了安全起见,你应该这样做

      static char const*const a[]

      这禁止修改数据要修改的表中的所有指针。

      顺便说一句,我更喜欢在类型名之后写const,这样一目了然const 的应用位置,即它的左侧。

      【讨论】:

      • 好评。你似乎暗示如果他同时写两个 const 我是正确的吗?
      【解决方案3】:

      在您的示例中,指针本身可以被视为线程安全的。它会被初始化一次,以后不会被修改。

      但是,指向的内存内容根本不是线程安全的。

      【讨论】:

      • 假设没有导致缓冲区溢出或欠载的编程错误,内存不是线程安全的前提是什么?
      • 对共享资源(在本例中为内存)的并发访问可能会导致某些损坏或不良行为。为避免此类问题,必须以某种方式序列化对该资源的访问。通常这可以使用互斥锁或任何更高级别的对象(如临界区)来实现。欲了解更多信息,这里有一些有趣的信息:c2.com/cgi/wiki?ThreadSafeen.wikipedia.org/wiki/Thread_safety
      • 指针可以修改。实际字符串中的字符不能。
      • 那只是写作。多个线程可以毫无问题地同时读取任何给定的内存块。在这种情况下,如果所有数据都是 const(指针和内容),那么就没有线程安全问题——如果他确保它只被初始化一次。
      • -1 const foo* 表示指针指向一个不能改变的foo,不是表示指针本身不能改变。
      【解决方案4】:

      在此示例中,a 不是 const。它是一个指向const 字符串的指针数组。如果你想使a 本身为const,你需要:

      static const char *const a[] = {"foo","bar","egg","spam"};
      

      无论是否为const,如果您不从其中任何一个线程写入数据,从多个线程读取数据总是是安全的。

      附带说明一下,声明指向常量字符串的指针数组通常是个坏主意,尤其是在可能用于共享库的代码中,因为它会导致大量重定位,并且数据不能位于实际的常量部分.更好的技术是:

      static const char a[][5] = {"foo","bar","egg","spam"};
      

      选择了 5 以使您的所有字符串都适合。如果字符串的长度是可变的,并且您不需要快速访问它们(例如,如果它们是像 strerror 这样的函数返回的错误消息),那么像这样存储它们是最有效的:

      static const char a[] = "foo\0bar\0egg\0spam\0";
      

      您可以通过以下方式访问nth 字符串:

      const char *s;
      for (i=0, s=a; i<n && *s; s+=strlen(s)+1);
      return s;
      

      请注意,最后的 \0 很重要。它使字符串末尾有两个 0 字节,因此如果 n 超出范围,则停止循环。或者,您可以提前对n 进行边界检查。

      【讨论】:

      • 为什么编译器不能把这些数据放在常量部分?数组项的逐位值要到加载时才能知道,但加载器在填充内容时应该没有问题。即使在代码执行期间也期望常量部分可重定位的系统是否存在问题?跨度>
      • 如果加载器在加载时对其进行了修改,我不会将其称为常量部分。它会导致每个使用该库的进程的物理内存命中和大量的加载时间开销。实际常量部分是指磁盘上实际库文件的共享内存映射图像。如果您认为我应该在答案中澄清这一点,我会的。
      【解决方案5】:

      static const char *a[] = {"foo","bar","egg","spam"};

      在 C 中始终是线程安全的:结构在编译时已经创建,因此在运行时不会采取额外的操作,因此不可能出现竞争条件。

      但请注意 C++ 兼容性。静态 const 对象将在第一次进入函数时被初始化,但语言不保证初始化是线程安全的。 IOW 当两个不同的线程同时进入函数并尝试并行初始化对象时,这对竞争条件开放。

      但即使在 C++ 中,POD(普通旧数据:不使用 C++ 特性的结构,如您的示例)也会以 C 兼容方式运行。

      【讨论】:

      • 任何时候一个线程都可以做,例如a[0] = "faa",所以不,如果你提供一些额外的保证,这只是线程安全的,但这本身不是线程安全的。
      • @Jens Gustedt:是的,但我对这个问题的解读是初始化是否是线程安全的。初始化是。用法 - 适用常规规则。
      猜你喜欢
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      • 1970-01-01
      • 2011-11-18
      • 2010-11-19
      • 2011-01-25
      • 1970-01-01
      • 2014-12-24
      相关资源
      最近更新 更多