17

在 XSH 2.9.1中,wctomb被列为不需要线程安全的函数之一。但是,相反的转换函数 ,mbtowc没有出现在列表中。在具有使用移位状态的编码的实现中,两者都没有线程安全的 API,并且要求一个是线程安全的而另一个不是,这是没有意义的,而如果不禁止有状态的编码,两者都不能是线程安全的。

对于wcstombs(在列表中)和mbstowcs(不在列表中)也是如此。由于这两个函数都对以初始移位状态开始和结束的整个字符串进行操作,因此它们不是有状态的,它们的 API 是线程安全的,同样,将一个方向指定为线程安全但不是指定方向是没有意义的其他。

任何人都可以对此有所了解吗?

4

4 回答 4

3

正如您在问题中指出的那样,wctomb具有或至少被允许具有(隐藏的)“转变状态”:请参阅http://pubs.opengroup.org/onlinepubs/009695399/functions/wctomb.html并将其与wcrtombhttp ://pubs.opengroup.org/onlinepubs/009695399/functions/wcrtomb.html有一个明确的“状态”指针。

POSIX 基本上允许程序员将wctomb调用实现为wcrtomb使用static变量来保持移位状态。如果该变量不是每个线程项,则它不是线程安全的。

(所有这些都很明显,并且包含在您的问题中,为了清楚起见,我只是在这里重复一遍)

请注意,没有任何参数可以wctomb让您对隐藏的移位状态(如果有的话)进行任何形式的显式控制。具体来说,您无法将其重置为启动状态。

但现在看看mbtowchttp ://pubs.opengroup.org/onlinepubs/009695399/functions/mbtowc.html文字说:

对于依赖于状态的编码,该函数通过调用其字符指针参数 s 为空指针而置于其初始状态。使用 s 而非空指针的后续调用将导致函数的内部状态根据需要进行更改。如果编码具有状态依赖性,则以 s 作为空指针的调用将导致此函数返回非零值,否则返回 0。如果实现使用特殊字节来改变移位状态,这些字节不应产生单独的宽字符代码,而是应与相邻字符组合。

也就是说,它们为您提供了一种检测和控制隐藏状态(如果存在)的显式方法。因此,即使它存在并且不是特定于线程的,您也可以按原样“进行自己的控制”。

虽然我不能发誓,但我认为这就是为什么mbtowc没有被列为非线程安全的原因。

(不过,这不是我在标准中编写文本的方式!再说一次,如果我有我的方式,很多这些功能甚至都不存在。:-))

于 2012-03-13T07:15:57.613 回答
2

基本的不对称性是 ISO C 要求宽字符具有固定宽度(所有字符都相同),并且编码没有移位状态。相比之下,多字节编码取决于语言环境,并且可能具有不同的字符宽度以及转换状态。

所有四个函数都在调用之间保留了内部状态(mbstowcs并且wcstombs还必须这样做,因为它们只转换指定数量的字节,而不是在初始移位状态下完成的完整字符串)。

在字符串转换的情况下,内部状态的组成存在细微差别。对于mbstowcs,在一次调用中转换了整数个宽字符。这是因为宽字符有固定的宽度,也因为n调用的参数是用字符指定的,而不是字节。相反,对于wcstombs参数n是用字节指定的,而不是多字节字符。因此,保持状态wcstombs不仅必须包括移位状态,还必须包括部分输出的多字节字符的剩余部分。因为状态是多部分的,所以在没有额外锁定的情况下,对它的操作(加载和存储)在典型架构上不会是原子的。

在这一点上,重要的是要提醒自己“线程安全”在 POSIX 中具有相当的技术含义,即并行调用在逻辑上是可序列化的。这并不意味着并行使用一定非常有用。由于所有四个函数都保持内部状态,很难想象一个调用者(一次)从左到右处理单个线性字符串,但将调用分散到多个线程中。ISO C 89/90 修正案 1 中引入 、 和 见证了这一点,该mbrtowc标志专门代表wcrtomb“可重入”。mbsrtowcswcstombsr

我无法准确解释为什么具有“原子可访问”的内部状态应该更容易使相应的调用更容易以线程安全的方式实现(因为有时必须在单个调用期间进行多次访问、加载和存储),但也许是因为额外锁定(和重新加载)的负担只施加在发生实际转移状态的很少访问的代码分支中。

还有另一个问题需要解释。并发线程可能会调用setlocale更改LC_CTYPE语言环境的字符编码 () 类别。ISO C 标准规定,这样的动作会导致当前状态(顺便说一下,甚至使用 捕获的状态wcrtomb)变得未定义。这是因为不同语言环境的转换状态可能不会以有用或指定的方式相互映射。尽管这是一个线程场景,它有可能破坏“可重入”函数系列,但它不一定对正式的线程安全实现构成障碍,因为可以在每次调用期间缓存语言环境设置。

于 2012-03-19T22:32:37.653 回答
1

C 程序“启用 Unicode”的常用方法是wchar_t在任何地方使用char并调用窄字符标准库函数的宽字符版本。我喜欢这种方法,因为很明显,类型变量wchar_t*要么指向wchar_t对象,要么指向宽字符串,但类型变量char*可以指向char对象、本机字符编码中的窄字符串或任何类型的多字节字符串数十种支持的字符编码。有这么多根本不同的含义char*,程序员必须非常小心,例如,不要将 UTF-8 编码的多字节字符串传递给需要窄字符串的函数,或者将当前编码的多字节字符串传递给需要 UTF-8-编码的字符串。也许是确保这一点的想法mbtowcmbstowcs(将多字节字符串转换为宽字符串的函数)是线程安全的,但不是将宽字符串转换为多字节字符串的函数,是为了说服程序员始终将字符串数据以宽字符格式保存在程序内存中,其中每个字符都准确表示执行字符集的一个成员,而不是使用可能不同的字符编码的窄字符串和多字节字符串的混合。也许标准作者认为这是更有用的方法,或者会更常见。

如果您考虑用 C 编写支持 Unicode 的多线程服务器软件,那么遵循以宽字符串格式保存字符串数据的模式有助于强制区分“从线路”读取的字符串数据和程序内存中的字符串数据。每当收到包含字符串数据有效负载的新消息时,解析消息及其字符串有效负载的 C 例程可以使用具有多字节到宽字符转换函数的窄字符 I/O 函数将字符串读入程序存储器。如果多个线程解析传入的消息——这是典型的——那么非常希望mbstowcs线程安全。

于 2011-01-28T02:13:58.397 回答
1

我认为这只是基于这样一个假设,即使用一组宽字符编码数据,由于代码点的固定宽度,程序员可以预测每个线程需要分配/释放多少内存。但是当朝着另一个方向前进时,根据编码,可能无法“预测”需要提前分配多少内存,从而为错误创造了更大的空间。

更新:在找到标准的旧版本后,我注意到 wctomb() 函数的手册页中的措辞有所不同:“wctomb() 函数不需要可重入。不需要的函数可重入不需要是线程安全的。” 我认为这暗示了标准中的另一个隐含假设: mbtowc() 是或应该是可重入的......

于 2011-01-28T02:17:33.123 回答