基本的不对称性是 ISO C 要求宽字符具有固定宽度(所有字符都相同),并且编码没有移位状态。相比之下,多字节编码取决于语言环境,并且可能具有不同的字符宽度以及转换状态。
所有四个函数都在调用之间保留了内部状态(mbstowcs
并且wcstombs
还必须这样做,因为它们只转换指定数量的字节,而不是在初始移位状态下完成的完整字符串)。
在字符串转换的情况下,内部状态的组成存在细微差别。对于mbstowcs
,在一次调用中转换了整数个宽字符。这是因为宽字符有固定的宽度,也因为n
调用的参数是用字符指定的,而不是字节。相反,对于wcstombs
参数n
是用字节指定的,而不是多字节字符。因此,保持状态wcstombs
不仅必须包括移位状态,还必须包括部分输出的多字节字符的剩余部分。因为状态是多部分的,所以在没有额外锁定的情况下,对它的操作(加载和存储)在典型架构上不会是原子的。
在这一点上,重要的是要提醒自己“线程安全”在 POSIX 中具有相当的技术含义,即并行调用在逻辑上是可序列化的。这并不意味着并行使用一定非常有用。由于所有四个函数都保持内部状态,很难想象一个调用者(一次)从左到右处理单个线性字符串,但将调用分散到多个线程中。ISO C 89/90 修正案 1 中引入 、 和 见证了这一点,该mbrtowc
标志专门代表wcrtomb
“可重入”。mbsrtowcs
wcstombs
r
我无法准确解释为什么具有“原子可访问”的内部状态应该更容易使相应的调用更容易以线程安全的方式实现(因为有时必须在单个调用期间进行多次访问、加载和存储),但也许是因为额外锁定(和重新加载)的负担只施加在发生实际转移状态的很少访问的代码分支中。
还有另一个问题需要解释。并发线程可能会调用setlocale
更改LC_CTYPE
语言环境的字符编码 () 类别。ISO C 标准规定,这样的动作会导致当前状态(顺便说一下,甚至使用 捕获的状态wcrtomb
)变得未定义。这是因为不同语言环境的转换状态可能不会以有用或指定的方式相互映射。尽管这是一个线程场景,它有可能破坏“可重入”函数系列,但它不一定对正式的线程安全实现构成障碍,因为可以在每次调用期间缓存语言环境设置。