我有一个由GUI线程和网络线程使用的Qt对象。它看起来像:
QString User::Username()
{
QMutexLocker locker(&mutex);
return username;
}
void User::SetUsername(const QString &newUsername)
{
QMutexLocker locker(&mutex);
username = newUsername;
}
QString User::Password()
{
QMutexLocker locker(&mutex);
return password;
}
...
GUI和网络线程都可以使用该对象(例如,在屏幕上显示用户名,并获取通过网络发送的用户名)。
我担心出了问题,因为对象中的每个方法都有一个QMutexLocker行,以使其线程安全。
以这种方式使用QMutexLocker是否可以接受,或者代码结构严重?
答案 0 :(得分:3)
您应该分别使用QReadWriteLock和QReadLocker或QWriteLocker。因此,如果只有读取线程,则不会锁定任何线程。
如果该类的某些字段被非常频繁地更改,并且不会更改该类的任何其他状态,则可能需要为其提供自己的专用锁。
答案 1 :(得分:1)
我认为你可能会以错误的方式处理事情。序列化每个方法调用将“排序”工作,但它不能可靠地处理添加或删除User对象等操作。例如,如果主线程删除了User对象,则网络线程小心锁定互斥锁无关紧要,因为在互斥锁操作返回后,网络线程将尝试访问(现已删除)用户对象,并尝试读取或写入释放的内存将导致您的应用程序崩溃(或者更糟糕的是,有时神秘地做错误的事情)。
这是一个更好的方法(假设User对象相当小):而不是让网络线程和I / O线程共享相同的User对象,并尝试序列化对象的所有访问方法级别,您最好将每个User对象的单独副本提供给I / O线程。然后,当一个线程更改其User对象的本地副本时,它应该向包含更新对象的副本的另一个线程发送消息,并且当另一个线程接收到该消息时,它可以更新其本地副本以再次匹配。这样,每个线程都拥有对其自己的本地User对象集的独占读/写访问权限,并且可以在不进行任何锁定的情况下读/写它们。这也允许每个线程随意添加或删除对象(只要它之后向另一个线程发送更新消息,所以另一个线程将跟随)。
答案 2 :(得分:0)
我认为更好更清洁的方法是拥有一个“安全部分”
updateUser( User ) {
User.acquireLock()
User.SetUsername(newUsername)
User.Password()
< more operations here >
User.releaseLock()
}
这样做的好处是你只能锁定一次互斥锁(这是一项昂贵的操作)。