我想确认我对线程的理解以及在C ++中通过引用传递。以下函数线程是否安全?
QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);
...
bool AreStringsEqual(QString const &stringA, QString const &stringB)
{
if(stringA == stringB)
{ return true; }
return false;
}
我认为是线程安全的。如果有人能确认我的思考过程,或者告诉我我不知道我在说什么,我会喜欢它。
进程内存中有两个sA和sB副本。在Thread1的堆栈上创建一个集合,在Thread2的堆栈上创建第二个集合。因为我们通过引用传递,所以每个线程在内存中只需要一组sA和sB来执行函数调用。
如果我们通过值传递,那么在某个时间点,进程内存中最多可能有四个sA和sB副本(每个线程有两个集合),其中两个线程都在函数内交换处理器控制调用
在任何情况下都不会共享内存,因此该函数是线程安全的。
很抱歉,如果这个问题非常简单,那么线程就会让我的脑子大吃一惊:)
的PRI
答案 0 :(得分:3)
没有理由说两个线程不能保存对相同字符串的引用。
此函数不是线程安全的,因为语句if(stringA == stringB)
不是原子的。
首先,您从内存中获取stringA
,然后才{@ 1}}。
让我们留下string B
。
你获取stringA == stringB == 2
,然后有一个上下文切换,stringA和stringB都变为3.然后你获取stringA
。您的函数将返回false(因为stringB
),尽管2 != 3
一直等于stringA
。
答案 1 :(得分:2)
您的问题在sA
和sB
被宣布的位置有点模糊。听起来它们是在一个函数内声明的,在这种情况下,你要纠正每个线程都拥有它自己的sA
和sB
版本。但是,如果它们在全球范围内宣布的奇怪机会,情况并非如此。如果我正确地理解了你的问题,你的意思是两者都是在本地范围内宣布的,所以你的第一点是正确的。出于同样的原因,你的第二点也是正确的。
但你的第三点是棘手的。在您的特定情况下,没有共享内存,因此您的程序是一个线程安全的"程序(不确定这是否是一个很好的方式来表达它)。但是,函数AreStringsEqual
不是线程安全的。在将来的某个时刻,您(或其他人)可以将该函数与 共享的数据一起使用,并且该函数本身不会保护自己不受此用途的影响。
答案 2 :(得分:2)
除非QString
指定operator==
线程安全,否则该函数不线程安全。 AreStringsEqual
的实现本身不会保护数据。
您正在通过此实现将线程安全的责任放在客户端上。客户端必须确保参数和参数的内部数据在AreStringsEqual
时不会发生变异(例如,由另一个线程)。因此,他们可能会发现自己不必要的副本。必须如何实现这一点必须由QString
的实施决定。即使std::string
实施也有很大差异=)
对于并发上下文中的字符串,通常会在将字符串移动到并发上下文之前获取副本。如果真的需要共享,你需要一些东西来保护它(比如锁)。对于原始集合(例如std::string
和std::vector
),您将希望避免在每次访问时锁定,因为它会破坏性能并且可能很容易失败。因此,如果必须共享非显式线程安全的对象,通常会复制或锁定。
因此,AreStringsEqual
的实现不是线程安全的(再次,除非bool QString::operator==(const QString&) const
保证是线程安全的。)
但是,AreStringsEqual
的用法:
QString sA = "hello";
QString sB = "world";
bool someFlag = AreStringsEqual(sA,sB);
对于大多数字符串实现都没问题,因为参数及其数据对于线程是本地的。
答案 3 :(得分:1)
如果线程之间共享sA和sB,则该函数不是线程安全的。
很可能在一个线程中执行函数AreStringsEqual期间,另一个线程尝试修改sA或sB或两者的值,然后会出现Race条件。
虽然您的函数没有修改值,但函数外部的代码可以。
因此最好使用pass by value,因为该函数将在堆栈上具有本地副本 这保证是线程安全的
答案 4 :(得分:0)
首先,如果它们总是具有相同的值,那么为什么你需要两个相同字符串的副本并不清楚。
根据你描述的上下文,它可能是线程安全的,但只是查看它自己的函数,它不是线程安全的,因为在执行if条件时,字符串的值可能已经改变了。