sprintf线程安全吗?

8

sprintf是线程安全的吗?

//Global log buffer 
char logBuffer[20];

logStatus (char * status, int length)
{
  snprintf(logBuffer, 19, status);
  printf ("%s\n", logBuffer);
}

这个函数的线程安全性完全取决于snprintf/sprintf的线程安全性。
更新: 感谢您的回答。 如果实际内容混乱了,我不介意。但是想确认当多个线程尝试写入logBuffer时,sprintf不会导致内存损坏/缓冲区溢出超过20个字节的情况。

1
如果您的实际需求是将打印的字符串限制在不超过19个字符,则使用printf("%.19s\n", status); - Michael Burr
3
实际的应用程序很复杂,这里很难进行详细解释。这个函数是代码的极简版本!希望你能理解 :-) - Jay D
7个回答

16

在多个线程中使用snprintf()没有问题。但是这里你正在写入一个共享的字符串缓冲区,我假设这个缓冲区在各个线程之间是共享的。

因此,你使用这个函数是不安全的。


3

"在多个线程中使用snprintf()没有问题。"

这是不正确的。

至少在POSIX函数的情况下,这是不正确的。

所有标准的vararg函数都不是mt-safe的 - 包括所有printf()家族 (1),但也包括其他所有可变参数函数 (2)。

  1. 例如,sprintf()是 "MT-Safe locale|AS-Unsafe heap|AC-Unsafe mem",这意味着如果区域设置异步设置或使用异步取消线程,则可能会失败。换句话说,在MT环境中使用此类函数时必须特别注意。

  2. va_arg不是mt-safe: |MT-Safe race:ap|AS-Safe|AC-Unsafe corrupt| - 这意味着需要锁定交错。

此外,明显的是,即使完全mt-safe的功能也可能以不安全的方式使用 - 例如,如果两个或更多线程操作相同的数据/内存区域。


你有证据表明它们不是线程安全的吗?当我谷歌搜索时,似乎它们是线程安全的。 - Mawg says reinstate Monica
@Dijkgraaf,我和Mawg says reinstate Monica's有同样的问题。 - John
@John 我想你应该是要回复vtomazzi,我只是编辑了他的答案并没有发布。 - Dijkgraaf

3

您的问题存在错误的前提。即使sprintf本身可以在同一时间从多个线程中安全调用(我确信它可以),但您的代码未保护全局变量。标准库无法在这方面帮助您。


3
您的代码存在几个问题:
  1. 您使用 snprintf 的方式非常可疑。不要仅仅为了复制字符串而使用它。通常不要将动态分配的字符串与任何内容一起作为格式传递给任何一个 printf 函数。它们会解释这些内容,如果其中有任何类似于 %-format 的内容,则会出现问题。
  2. 请勿像您所做的那样使用 static 缓冲区。这显然既不安全也不可重入。
  3. 直接使用适当的格式调用 printf,或者将该调用替换为 puts
此外,Linux 遵循 POSIX 标准,该标准要求标准 IO 函数是线程安全的。

3
关于您的更新,关于不必担心logBuffer内容被破坏:
我不确定为什么您想避免使用本地分配的缓冲区或某些同步机制使函数完全线程安全,但如果您想知道POSIX对此有何看法,这里是(http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_11):

应用程序应确保通过一个以上的控制线程(线程或进程)访问任何内存位置都受到限制,以便没有控制线程可以在另一个控制线程可能正在修改它时读取或修改内存位置。使用同步线程执行并与其他线程同步内存的函数限制此类访问。[后跟提供同步的函数列表]

因此,POSIX规定您的程序需要确保多个线程不会同时修改logBuffer(或在一个线程中修改logBuffer而在另一个线程中读取它)。如果您不遵循这一点,则不能保证最坏的结果只是logBuffer中的数据混乱。根本没有任何承诺关于结果将是什么。我不知道Linux是否可能记录更具体的行为,但我怀疑它不会。

谢谢您的回答。但问题是关于在多个线程下使用相同缓冲区的sprintf行为,而不是如何编写多线程应用程序来使用缓冲区! - Jay D
2
@Jay D:所以你有多个线程同时读取/修改同一个缓冲区,而没有同步 - 它们只是通过sprintf()/snprintf()偶然这样做。在POSIX中,这种行为是未定义的;我不确定你还在寻找什么其他的东西。即使那是你可能遇到的最常见的问题,我不认为你会得到任何人说最糟糕的情况就是一个混乱的logBuffer - Michael Burr
@Michael Burr 我的程序有时会崩溃,当使用类似于这篇帖子中的代码片段时。 - John

1

它不是线程安全的,因为您sprintf的缓冲区在所有线程之间共享。


0

你有证据表明它们不是线程安全的吗?当我谷歌时,似乎它们是线程安全的。

我的先前回答已被删除(为什么?),所以我将尝试使用不同的方法再次回答:

  1. AC(异步取消线程):这显然是一个几乎所有“表面上是MT安全”的代码都可能失败的情况,因为线程在随机时间点中断,所以任何同步方法都不能保证正确工作(即任何形式的互斥锁都不能真正保证正确工作)

  2. 线程可以使用相同的malloc()区域,这意味着,如果其中一个线程失败了(即它会损坏malloc区域),那么所有连续的malloc()调用都可能导致严重错误-当然这取决于系统配置-但这也意味着,没有人应该假设畸形的内存(de)分配是安全的。

由于所有系统都提供使用不同本地设置的选项,很明显,对“语言环境”设置的异步更改可能会导致错误...

问候。


1
为什么不直接编辑您的其他答案或回复那个答案下面的评论,这样人们就不会误解您是在回复评论了。以这种方式回复评论似乎会让人误认为您在回答初始问题,顺便说一句,这可能是您之前的回答被删除的原因。 - Petter Friberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接