我知道答案周围有很多类似的问题,但由于我仍然不理解这个特例,我决定提出一个问题。
我所拥有的是shared_ptrs到动态分配的数组(MyVector)的映射。我想要的是有限的并发访问而无需锁定。我知道地图本身不是线程安全的,但我一直认为我在这里做的应该没问题,这是:
我在这样的单线程环境中填充地图:
typedef shared_ptr<MyVector<float>> MyVectorPtr;
for (int i = 0; i < numElements; i++)
{
content[i] = MyVectorPtr(new MyVector<float>(numRows));
}
初始化之后,我有一个从元素中读取的线程和一个替换shared_ptrs指向的线程。
主题1:
for(auto i=content.begin();i!=content.end();i++)
{
MyVectorPtr p(i->second);
if (p)
{
memory_use+=sizeof(int) + sizeof(float) * p->number;
}
}
主题2:
for (auto itr=content.begin();content.end()!=itr;++itr)
{
itr->second.reset(new MyVector<float>(numRows));
}
过了一会儿,我在两个线程中的一个中得到了一个seg错误或一个double free。不知怎的,这并不令人惊讶,但我仍然没有得到它。
我认为这会起作用的原因是:
有人可以释放我吗?非常感谢!
如果有人想试试,这是完整的代码示例:
#include <stdio.h>
#include <iostream>
#include <string>
#include <map>
#include <unistd.h>
#include <pthread.h>
using namespace std;
template<class T>
class MyVector
{
public:
MyVector(int length)
: number(length)
, array(new T[length])
{
}
~MyVector()
{
if (array != NULL)
{
delete[] array;
}
array = NULL;
}
int number;
private:
T* array;
};
typedef shared_ptr<MyVector<float>> MyVectorPtr;
static map<int,MyVectorPtr> content;
const int numRows = 1000;
const int numElements = 10;
//pthread_mutex_t write_lock;
double get_cache_size_in_megabyte()
{
double memory_use=0;
//BlockingLockGuard guard(write_lock);
for(auto i=content.begin();i!=content.end();i++)
{
MyVectorPtr p(i->second);
if (p)
{
memory_use+=sizeof(int) + sizeof(float) * p->number;
}
}
return memory_use/(1024.0*1024.0);
}
void* write_content(void*)
{
while(true)
{
//BlockingLockGuard guard(write_lock);
for (auto itr=content.begin();content.end()!=itr;++itr)
{
itr->second.reset(new MyVector<float>(numRows));
cout << "one new written" <<endl;
}
}
return NULL;
}
void* loop_size_checker(void*)
{
while (true)
{
cout << get_cache_size_in_megabyte() << endl;;
}
return NULL;
}
int main(int argc, const char* argv[])
{
for (int i = 0; i < numElements; i++)
{
content[i] = MyVectorPtr(new MyVector<float>(numRows));
}
pthread_attr_t attr;
pthread_attr_init(&attr) ;
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_t *grid_proc3 = new pthread_t;
pthread_create(grid_proc3, &attr, &loop_size_checker,NULL);
pthread_t *grid_proc = new pthread_t;
pthread_create(grid_proc, &attr, &write_content,(void*)NULL);
// to keep alive and avoid content being deleted
sleep(10000);
}
答案 0 :(得分:2)
我认为只要操作是原子的,同时更改地图的单个元素就可以了。
除非你有像std::atomic
这样的原子类型,否则更改地图中的元素不是原子的。
我认为我对shared_ptr所做的操作(增量引用计数,线程1中的减量引用计数,线程2中的重置)是原子的。
这是正确的。不幸的是,你也在改变底层指针。那个指针不是原子的。由于它不是原子的,你需要同步。
您可以做的一件事是使用std::shared_ptr
引入的atomic free functions。这样您就可以避免使用mutex
。
答案 1 :(得分:0)
<强> TL; DR; 强>
更改std::map
不是线程安全的,而使用std::shared_ptr
关于其他引用是。
您应该使用适当的同步机制保护访问有关读/写操作的地图,例如: std::mutex
。
此外,如果std::shared_ptr
引用的实例的状态应该更改,则需要保护它免受数据争用的影响(如果从并发线程访问它)。
MyVector
是一种过于天真的实施方式。
答案 2 :(得分:0)
让我们展开在线程1上运行的MyVectorPtr p(i->second);
:
要求的构造函数是:
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) = default;
可能归结为underlying shared pointer
和reference count
的2个作业。
线程2很可能会删除共享指针,而在线程1中指针被分配给p
。存储在shared_ptr
内的底层指针不是原子。
因此,std::shared_ptr
的使用不是线程安全的。只要您不更新或修改基础指针,它就是线程安全的。