为了账号安全,请及时绑定邮箱和手机立即绑定

线程安全与可重入

/ 猿问

线程安全与可重入

撒科打诨 2019-12-09 15:00:11

最近,我问了一个问题,标题为“ malloc线程安全吗?” ,然后我问:“ malloc是否可重入?”


我的印象是所有重入者都是线程安全的。


这个假设错了吗?


查看完整描述

3 回答

?
莫回无

可重入函数不依赖于C库头中公开的全局变量..以C中的strtok()vs strtok_r()为例。


有些函数需要在某个位置存储“正在进行的工作”,可重入函数允许您在线程自己的存储中而不是在全局中指定此指针。由于此存储是调用函数所独有的,因此可以中断和重新输入(重新进入),并且由于在大多数情况下,此功能不需要执行该函数实现的互斥,因此通常认为它们是线程安全。但是,按定义不能保证。


但是,errno在POSIX系统上的情况略有不同(并且在所有这些如何工作的任何解释中,它都是奇怪的东西):)


简而言之,可重入通常意味着线程安全(如“如果您正在使用线程,请使用该函数的可重入版本”),但是线程安全并不总是意味着可重入(或相反)。当您考虑线程安全时,并发就是您需要考虑的问题。如果必须提供一种锁定和互斥的方法来使用某个函数,则该函数本质上并不是线程安全的。


但是,并非所有功能都需要检查。malloc()不需要重入,它不依赖任何给定线程的入口点范围之外的任何东西(并且它本身是线程安全的)。


如果不使用互斥体,futex或其他原子锁定机制,则返回静态分配值的函数将不是线程安全的。但是,如果他们不会被打扰,则无需重新进入。


即:


static char *foo(unsigned int flags)

{

  static char ret[2] = { 0 };


  if (flags & FOO_BAR)

    ret[0] = 'c';

  else if (flags & BAR_FOO)

    ret[0] = 'd';

  else

    ret[0] = 'e';


  ret[1] = 'A';


  return ret;

}

因此,如您所见,让多个线程使用该线程而不进行某种锁定将是一场灾难..但是重新进入没有任何目的。当某些嵌入式平台上动态分配内存成为禁忌时,您会遇到这种情况。


在纯函数式编程中,可重入通常并不意味着线程安全,它取决于传递给函数入口点,递归等的已定义或匿名函数的行为。


更好的“线程安全”方法对于并发访问是安全的,这更好地说明了这一需求。


查看完整回答
反对 回复 2019-12-09
?
LEATH

函数可以是可重入的,线程安全的,或者两者均可。


维基百科上有关线程安全性和可重入性的文章非常值得一读。这里有一些引用:


在以下情况下,函数是线程安全的:


它仅以确保多个线程同时安全执行的方式操作共享数据结构。


在以下情况下,函数是可重入的:


它可以在执行过程中的任何时候中断,然后在之前的调用完成执行之前安全地再次调用(“重新输入”)。


作为可能重新进入的示例,维基百科给出了一个设计为由系统中断调用的函数的示例:假设当另一个中断发生时该函数已经在运行。但是不要仅仅因为您不使用系统中断进行编码就认为您是安全的:如果使用回调或递归函数,则在单线程程序中可能会遇到重入问题。


避免混淆的关键是可重入仅指一个正在执行的线程。从不存在多任务操作系统的时代开始就是一个概念。


例子


(从维基百科的文章稍作修改)


示例1:不是线程安全的,不是可重入的


/* As this function uses a non-const global variable without

   any precaution, it is neither reentrant nor thread-safe. */


int t;


void swap(int *x, int *y)

{

    t = *x;

    *x = *y;

    *y = t;

}

示例2:线程安全,不可重入


/* We use a thread local variable: the function is now

   thread-safe but still not reentrant (within the

   same thread). */


__thread int t;


void swap(int *x, int *y)

{

    t = *x;

    *x = *y;

    *y = t;

}

示例3:不是线程安全的,可重入


/* We save the global state in a local variable and we restore

   it at the end of the function.  The function is now reentrant

   but it is not thread safe. */


int t;


void swap(int *x, int *y)

{

    int s;

    s = t;

    t = *x;

    *x = *y;

    *y = t;

    t = s;

}

示例4:线程安全,可重入


/* We use a local variable: the function is now

   thread-safe and reentrant, we have ascended to

   higher plane of existence.  */


void swap(int *x, int *y)

{

    int t;

    t = *x;

    *x = *y;

    *y = t;

}


查看完整回答
反对 回复 2019-12-09
?
慕函数4003404

这取决于定义。例如,Qt使用以下内容:


即使调用使用共享数据,也可以从多个线程同时调用线程安全*函数,因为对共享数据的所有引用都已序列化。


一个可重入函数也可以从多个线程同时调用,但只有当每个调用使用自己的数据。


因此,线程安全函数始终是可重入的,但是重入函数并不总是线程安全的。


通过扩展,如果可以从多个线程安全地调用其成员函数,则该类称为可重入的,只要每个线程使用该类的不同实例即可。如果可以从多个线程安全地调用其成员函数,则该类是线程安全的,即使所有线程都使用该类的相同实例也是如此。


但他们也警告:


注意:多线程域中的术语尚未完全标准化。POSIX使用可重入和线程安全的定义,这些定义对其C API有所不同。当将其他面向对象的C ++类库与Qt一起使用时,请确保理解定义。


查看完整回答
反对 回复 2019-12-09

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信