Qt线程安全类,可重⼊类,不可重⼊类,线程安全函数,可重
⼊函数
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"));
}
可以看出,三个概念的安全系数是递减的关系。
对于函数的可重⼊,和类有些不同
可重⼊函数只是线程安全函数的⼀种,详见:
在多线程编程和信号处理过程中,经常会遇到可重⼊(reentrance)与线程安全(thread-safe)。
很多⼈纠结于reentrance和thread-safe两个概念理解纠缠不清。我想救我对reentrance和thread-safe的理解作个总结
⼀下以Posix协议的接⼝函数为例进⾏说明:
⼀、可重⼊(reentrance)
⾸先来看下APUE中,列出的可重⼊函数:
accept fchmod lseek sendto stat
access fchown lstat setgid symlink
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
dup2getuid 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 lrand48strtok
dbm_store getgrgid getservbyport mrand48ttyname
dirname getgrnam getservent nftw unsetenv
dlerror gethostbyaddr getutxent nl_langinfo wcstombs
drand48gethostbyname 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中的解释,如果⼀个函数能够安全的同时被多个线程调⽤⽽得到正确的结果,那么,我们说这个函数是线程安全的。所谓安全,⼀切可能导致结果不正确的因素都是不安全的调⽤。
线程安全,是针对多线程⽽⾔的。那么和可重⼊联系起来,我们可以断定,可重⼊函数必定是线程安全的,但是线程安全的,不⼀定是可重⼊的。不可重⼊函数,函数调⽤结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调⽤,那么,这个不可重⼊函数就是转换成了线程安全。
线程安全,描述的是函数能同时被多个线程安全的调⽤,并不要求调⽤函数的结果具有可再现性。也
就是说,多个线程同时调⽤该函数,允许出现互相影响的情况,这种情况的出现需要某些机制⽐如互斥锁来⽀持,使之安全。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论