我看到了这样一句话:“有些函数本质上是线程安全的,例如
memcpy()
”
维基百科将“线程安全”定义为:
如果一段代码只以保证多个线程同时执行时对共享数据结构进行安全操作的方式来操作,那么它就是线程安全的。
好的。但是“本质上”是什么意思?它与继承有关吗?
memcpy()
”
维基百科将“线程安全”定义为:
如果一段代码只以保证多个线程同时执行时对共享数据结构进行安全操作的方式来操作,那么它就是线程安全的。
好的。但是“本质上”是什么意思?它与继承有关吗?
它与继承无关。这是一个非正式的表达方式,更像是指
“某些函数本质上就是线程安全的”。例如,一个不会涉及任何共享值/状态的函数本来就是线程安全的,也就是“本质上是线程安全的”。
memcpy()
的固有线程安全性也并非魔法。任何没有内部状态或“共享”副作用的函数都会是线程安全的,这就是为什么函数式编程非常适合并行编程的原因,其中所有函数都应该是“纯”的并且缺乏副作用。memcpy()
也不是没有副作用的,其核心目的是操作内存,如果在线程之间共享,则肯定不安全。 假设只要目标区域是不同的,那么无论一个或多个线程并行运行memcpy()
都没有问题。printf()
相比,该函数在单个(对于进程)输出流上生成字符。 它必须明确实现(根据POSIX的要求)以实现线程安全,而memcpy()
则不需要。本质上是线程安全的函数,无需针对线程做出任何特定设计决策即可安全使用,它之所以是线程安全的,是因为它执行的任务本身就具有线程安全性,而不是通过重新设计来强制实现线程安全。比如我编写一个非常简单的函数:
int cube(int x)
{
return x*x*x;
}
它本身是线程安全的,因为它没有读取或写入共享内存的方式。然而,我也可以通过特定的设计和同步使一个不安全的函数变得线程安全。比如说,我有一个类似于之前的函数:
void cubeshare()
{
static int x;
x = x * x * x;
printf("%l", x);
}
void cubesharesafe(mutex foo)
{
static int x;
lockmutex(foo);
x = x * x * x;
printf("%l", x);
unlockmutex(foo);
}
然而,这并不是本质上线程安全的,我们通过重新设计来强制实现它。实际示例通常比这更复杂,但我希望这能够在最简单的水平上给出一个想法。如果您有任何问题,请在下方评论。
memcpy
实现如果不用于写入可能被另一个线程使用的内存,也不读取可能被另一个线程写入的内存,则是非阻塞和线程安全的。使用DMA控制器执行大于某个大小的移动的实现可能比典型实现更快,但即使不同线程访问的所有内存区域完全不相交,也可能无法在没有操作系统帮助的情况下同时实现非阻塞和线程安全。 - supercatmemcpy
在本质上是线程安全的。 - Steve Jessop在使用memcpy
时,只有单个线程能够将特定源的写入传输到特定目的地。:因此具有最初设计的线程安全性。
固有的意味着:在不需要“调整”基本函数以实现目标的情况下,即实现线程安全的情况下。
如果多个线程可以同时干扰同一“通道”,那么您将面临与共享数据块相关的线程安全问题。
inherent的意思是“作为永久性存在于某物中”。它与继承无关。 默认情况下,或者已经有一些方法是线程安全的...为了保护或避免多任务问题... vector、hash table等是一些本质上是线程安全的示例类... 有一些函数是默认情况下是线程安全的,这并不令人困惑。
memcpy
不需要触及共享状态(因此在天真的情况下是线程安全的),但它以非同步的方式修改任意内存区域,这显然根本就不是线程安全的。 - Damonmemcpy
被Posix要求具有线程安全性。尽管维基百科上的引用可能不一定符合这个定义,但如果我们非常小心地理解“共享数据结构”的含义,它也可以符合这个定义。因此,只有当你不理解“线程安全”所定义的背景时才会认为它荒谬。由于在Java中,“线程安全”的通常含义与C中的通常含义不同,所以标签不幸地引起了混淆。 - Steve Jessop