閱讀455 返回首頁    go 阿裏雲 go 技術社區[雲棲]


深入理解可重入與線程安全

在多線程編程和信號處理過程中,經常會遇到可重入(reentrance)與線程安全(thread-safe)。

很多人糾結於reentrance和thread-safe兩個概念理解糾纏不清。我想救我對reentrance和thread-safe的理解作個總結

 

一、可重入(reentrance)

首先來看下APUE中,列出的可重入函數:

 

accept

fchmod

lseek

sendto

stat

access

fchown

lstat

setgid

symlink

aio_error

fcntl

mkdir

setpgid

sysconf

aio_return

fdatasync

mkfifo

setsid

tcdrain

aio_suspend

fork

open

setsockopt

tcflow

alarm

fpathconf

pathconf

setuid

tcflush

bind

fstat

pause

shutdown

tcgetattr

cfgetispeed

fsync

pipe

sigaction

tcgetpgrp

cfgetospeed

ftruncate

poll

sigaddset

tcsendbreak

cfsetispeed

getegid

posix_trace_event

sigdelset

tcsetattr

cfsetospeed

geteuid

pselect

sigemptyset

tcsetpgrp

chdir

getgid

raise

sigfillset

time

chmod

getgroups

read

sigismember

timer_getoverrun

chown

getpeername

readlink

signal

timer_gettime

clock_gettime

getpgrp

recv

sigpause

timer_settime

close

getpid

recvfrom

sigpending

times

connect

getppid

recvmsg

sigprocmask

umask

creat

getsockname

rename

sigqueue

uname

dup

getsockopt

rmdir

sigset

unlink

dup2

getuid

select

sigsuspend

utime

execle

kill

sem_post

sleep

wait

execve

link

send

socket

waitpid

_Exit & _exit

listen

sendmsg

socketpair

write

 

以上表中的這些函數,都是可重入的。

 

那麼究竟什麼是可重入函數呢?

 

我的理解:可重入函數,與多線程無關,即可重入概念並不依賴於多線程,可重入的提出時依據單一線程提出來的,當然,多線程可重入是他的擴展。一個函數被同一個線程調用2次以上,得到的結果具有可再現性(多次調用函數,得到的結果是一樣的)。那麼我們說這個函數是可重入的。

 

可重入,並不一定要是多線程的。可重入隻關注一個結果可再現性。在APUE中,可函數可重入的概念最先是在講signal的handler的時候提出的。此時進程(線程)正在執行函數fun(),在函數fun()還未執行完的時候,突然進程接收到一個信號sig, 此時,需要暫停執行fun(),要轉而執行sig信號的處理函數sig_handler(),那麼,如果在sig_handler()中,也恰好調用了函數fun().信號的處理是以軟終端的形式進行的,那麼,當sig_handler()執行完返回之後,CPU會繼續從fun()被打斷的地方往下執行。這裏講的比較特殊,最好的情況是,進程中調用了fun(),函數,信號處理函數sig_handle()中也調用了fun()。如果fun()函數是可重入的,那麼,多次調用fun()函數就具有可再現性。從而,兩次調用fun()的結果是正確的預期結果。非可重入函數,則恰好相反。

 

簡而言之,可重入函數,描述的是函數被多次調用但是結果具有可再現性。

 

如果fun(),中,使用了static變量、返回全局變量、調用非可重入函數等等,帶有全局性的操作,都將會導致2次以上調用fun()的結果的不可再現性(當然,有些時候使用了static、全局變量等等,不一定導致調用結果不可再現性)。隻要使調用結果具有可再現性,那麼該函數就是可重入的。

 

為了保證函數是可重入的,需要做到一下幾點:

1,不在函數內部使用靜態或者全局數據

2,不返回靜態或者全局數據,所有的數據都由函數調用者提供

3,使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據

4, 如果必須訪問全局數據,使用互斥鎖來保護

5,不調用不可重入函數

 

 

二,函數線程安全

 

看看APUE上,描述的非線程安全函數

asctime

ecvt

gethostent

getutxline

putc_unlocked

basename

encrypt

getlogin

gmtime

putchar_unlocked

catgets

endgrent

getnetbyaddr

hcreate

putenv

crypt

endpwent

getnetbyname

hdestroy

pututxline

ctime

endutxent

getnetent

hsearch

rand

dbm_clearerr

fcvt

getopt

inet_ntoa

readdir

dbm_close

ftw

getprotobyname

l64a

setenv

dbm_delete

gcvt

getprotobynumber

lgamma

setgrent

dbm_error

getc_unlocked

getprotoent

lgammaf

setkey

dbm_fetch

getchar_unlocked

getpwent

lgammal

setpwent

dbm_firstkey

getdate

getpwnam

localeconv

setutxent

dbm_nextkey

getenv

getpwuid

localtime

strerror

dbm_open

getgrent

getservbyname

lrand48

strtok

dbm_store

getgrgid

getservbyport

mrand48

ttyname

dirname

getgrnam

getservent

nftw

unsetenv

dlerror

gethostbyaddr

getutxent

nl_langinfo

wcstombs

drand48

gethostbyname

getutxid

ptsname

wctomb

 

 

 

If a function can be safely called by multiple threads at the same time, we say that the function is thread-safe

 

上麵一段話是APUE中的解釋,如果一個函數能夠安全的同時被多個線程調用而得到正確的結果,那麼,我們說這個函數是線程安全的。所謂安全,一切可能導致結果不正確的因素都是不安全的調用。

 

線程安全,是針對多線程而言的。那麼和可重入聯係起來,我們可以斷定,可重入函數必定是線程安全的,但是線程安全的,不一定是可重入的。不可重入函數,函數調用結果不具有可再現性,可以通過互斥鎖等機製,使之能安全的同時被多個線程調用,那麼,這個不可重入函數就是轉換成了線程安全。

 

線程安全,描述的是函數能同時被多個線程安全的調用,並不要求調用函數的結果具有可再現性。也就是說,多個線程同時調用該函數,允許出現互相影響的情況,這種情況的出現需要某些機製比如互斥鎖來支持,使之安全。

 

 

 

 

 


版權申明:
轉載文章請注明原文出處https://blog.csdn.net/feiyinzilgd/archive/2010/08/14/5811157.aspx

並請聯係譚海燕本人或者前往譚海燕個人主頁留言

最後更新:2017-04-02 06:51:24

  上一篇:go WIN7中修改Jar文件打開方式的方法
  下一篇:go MeeGo 中間件(middleware) 通信服務 之(一) 連接管理