前提:假设高级语言是C,不讨论汇编语言
 
1。先说terminology
可重入函数:       Reentrant Function
线程安全函数:   Thread-Safe Function
 
2。Reentrant 和 Thread-Safe 是相互独立的概念
也就是说函数可以是下列四种组合:
  • 仅Reentrant
  • 仅Thread-Safe
  • 既不Reentrant也不Thread-Safe
  • 既Reentrant也Thread-Safe

可重入函数要解决的问题是,不在函数内部使用静态或全局数据,不返回静态或全局数据,也不调用不可重入函数。

线程安全函数要解决的问题是,多个线程调用函数时访问资源冲突;很显然这里的资源是必须被threads共享的。

函数如果使用静态变量,通过加锁后可以转成线程安全函数,但仍然有可能不是可重入的的;比如strtok,因为其中有静态变量,通过加锁后必是线程安全的,但仍然不是可重入的,因为对第二字符串必将覆盖第一字符串。

strtok是既不reentrant,也不thread-safe。

加锁的strtok是不reentrant,但thread-safe。

而strtok_r是既reentrant,也thread-safe。

而可以重入但不是thread-safe的函数,我还没有找到:-)….
      实际上,如果reentrant,那么一定thread-safe。

3。例子

3.1 非可重入函数strtok 和可重入函数strtok_r

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char string[]="I am excellent! he he is a pig!";
  char *p;

printf("%sn", "");
  printf("%sn", "########EXAMPLE 0: show usage of strtok########");
  p=strtok(string, " ");
  do {
     printf("%sn", p);
     p=strtok(NULL, " ");
  } while (p);
  printf("string=%sn", string);

// to demonstrate lb points to next char following
  // non-token string
  char string_r[]=" I am  excellent!  he is a pig!";
  char *lb;
  printf("%sn", "");
  printf("%sn", "########EXAMPLE 1: show next char following a token########");
  printf("string_r="%s"n", string_r);
  printf("string_r starting at %08x, its length=0x%xn", string_r, strlen(string_r));

p=strtok_r(string_r, " ", &lb);
  do {
     if (lb != NULL)
       printf("reentrant: p=%-15s lb=%08x *lb=%c *lb=%dn", p, lb, *lb, *lb);
     else
       printf("reentrant: p=%-15s lb=%08x *lb=%c *lb=%dn", p, lb, 0, 0);
     p=strtok_r(NULL, " ", &lb);
  } while (p);
 
  // to demonstrate what is reentrant, for example, strtok_r
  char string_r_1[]=" I am excellent! he is a pig!";
  char string_r_2[]="hello:+my:;name:is:cj!+he+he";
  char *lb1, *lb2; 
  char *p1, *p2;
  printf("%sn", "");
  printf("%sn", "########EXAMPLE 2: show what is reentrant########");
  printf("string_r_1="%s"n", string_r_1);
  printf("string_r_1 starting at %08x, its length=0x%xn", string_r_1, strlen(string_r_1));
  printf("string_r_2="%s"n", string_r_2);
  printf("string_r_2 starting at %08x, its length=0x%xn", string_r_2, strlen(string_r_2));

  p1=strtok_r(string_r_1, " ", &lb1);
  p2=strtok_r(string_r_2, ":+;", &lb2);
  do {
     if (lb1 !=NULL)
       printf("p1=%-15s lb1=%08x *lb1=%c *lb1=%dn", p1, lb1, *lb1, *lb1);
     else
       printf("p1=%-15s lb1=%08x *lb1=%c *lb1=%dn", p1, lb1, 0, 0);
     if (lb2 !=NULL)
       printf("p2=%-15s lb2=%08x *lb2=%c *lb2=%dn", p2, lb2, *lb2, *lb2);
     else
       printf("p2=%-15s lb2=%08x *lb2=%c *lb2=%dn", p2, lb2, 0, 0);
     p1=strtok_r(NULL, " ", &lb1);
     p2=strtok_r(NULL, ":+;", &lb2);
  } while (p1 && p2);

return 0;
}

请特别注意黄颜色部分代码 ;这部分代码如果替换为strtok是不可能得到正确结果的;strtok_r是在同一thread中被调用的。
 
3.2 非线程安全例子和线程安全例子
/* thread-unsafe function */
int increment_counter()
{
static int counter = 0;

counter++;
return counter;
}

/* pseudo-code thread-safe function */
int increment_counter();
{
static int counter = 0;
static lock_type counter_lock = LOCK_INITIALIZER;

lock(counter_lock);
counter++;
unlock(counter_lock);
return counter;
}

参考:

  1. http://en.wikipedia.org/wiki/Thread-safety
  2. http://en.wikipedia.org/wiki/Reentrant_(subroutine)
Advertisements