Qt 线程安全类,可重入类,不可重入类,线程安全函数,可重入函数

本文详细介绍了Qt中线程安全类、可重入类和不可重入类的区别,强调了线程安全在多线程编程中的重要性。线程安全类如QMutex允许不同线程安全操作,可重入类确保不同实例在同一线程中的安全,而不可重入类如QWidget限制在主线程使用。同时,讨论了可重入函数和线程安全函数的概念,可重入函数关注结果的可再现性,线程安全函数则关注多线程调用的安全性。
摘要由CSDN通过智能技术生成

Qt 线程安全类,可重入类,不可重入类,线程安全函数,可重入函数
我们首先对类进行说明,可以看成是递进关系,参考C++ GUI Qt 4 Programming。

1、线程安全类:(同类同一对象)
不同线程对同一个类的对象进行操作,例如在不同线程调用同一个对象的类成员函数,是安全的,互不妨碍的,则说明该类是线程安全的,Qt中线程安全的类有QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、QSemaphore、QThreadStorage<T>以及QWaitCondition;

2、可重入类:(同类不同对象)
可重入类只能保证可以在不同线程中操作不同的此类的对象是安全的,不能保证,不同线程操作同一个此类对象,是安全的;绝大多数Qt的非图形界面类都符合一个并不太严格的要求:它们都必须是可重入的,及类的不同实例可同时用于不同的线程中;很多Qt的非图形用户界面类,包括QImage、QString和一些容器类,都使用了隐式共享作为一项优化技术,虽然这样的优化通常会让类变成不可重入的,但是Qt使用原子汇编语言语言指令来实现线程安全引用计数,这可以让Qt的隐式共享类变成可重入的;

3、不可重入类:
只能在一个线程中(一般是主线程)实例化对象,不能再其他线程中实例化该类的对象,可能是由于不同对象共享同一块内存,导致的;所有的QWidget他的子类都是不可重入的,所以qt中的界面类实例化对象只能在主线程中,也不要试图把界面类对象的所属线程更改到子线程中,更不要在子线程中直接操作界面类对象,要使用信号与槽技术或者调用

QMetaObject::invokeMethod(),具体原型参考Qt Assistant来实现在子线程中调用界面类的成员函数;

例如更改QLabel对象label的显示文本:

void MyThread::run()
{
    QMetaObject::invokeMethod(label, SLOT(setText(const QString &)), Q_ARG(QString, "Hello"));
}

可以看出,三个概念的安全系数是递减的关系。

对于函数的可重入,和类有些不同
可重入函数只是线程安全函数的一种,详见:

http://blog.csdn.net/youngkingyj/article/details/24433733

在多线程编程和信号处理过程中,经常会遇到可重入(reentrance)与线程安全(thread-safe)。

很多人纠结于reentrance和thread-safe两个概念理解纠缠不清。我想救我对reentrance和thread-safe的理解作个总结

 一下以Posix协议的接口函数为例进行说明:

一、可重入(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    sem_post    wait
execve    link    send    send    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中的解释,如果一个函数能够安全的同时被多个线程调用而得到正确的结果,那么,我们说这个函数是线程安全的。所谓安全,一切可能导致结果不正确的因素都是不安全的调用。

线程安全,是针对多线程而言的。那么和可重入联系起来,我们可以断定,可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。不可重入函数,函数调用结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调用,那么,这个不可重入函数就是转换成了线程安全。

线程安全,描述的是函数能同时被多个线程安全的调用,并不要求调用函数的结果具有可再现性。也就是说,多个线程同时调用该函数,允许出现互相影响的情况,这种情况的出现需要某些机制比如互斥锁来支持,使之安全。
 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值