再问STL中map的使用

软件和网站开发以及相关技术探讨
回复
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

再问STL中map的使用

#1

帖子 weihua2008 » 2008-11-26 16:35

//将struct作为map的成员,在将struct添加到map中后是否还可以自由修改struct的变量
#include<iostream>
#include<string>
#include<map>
using namespace std;
struct CTest
{
int i;
char a[16];
};
map<int ,CTest*> map_struct;
int main()
{
CTest* ct=new CTest;
ct->i=10;
strncpy(ct->a,"192.168.3.32",strlen("192.168.3.32"));
pair<map<int,CTest*>::iterator,bool> insert_pair;
insert_pair=map_struct.insert(map<int,CTest*>::value_type(ct->i,ct));
map<int,CTest*>::iterator iter;
if(true==insert_pair.second)
cout<<"插入数据成功\n";
else
cout<<"插入数据失败\n";

for(iter=map_struct.begin();iter!=map_struct.end();iter++)
cout<<iter->first<<"\t"<<(iter->second)->i<<"\t"<<(iter->second)->a<<endl;
//现在私下修改struct的数据
ct->i=199;
strncpy(ct->a,"192.168.3.31",strlen("192.168.3.31"));

///试验证明在不惊动map的情况下仍然可以对struct的变量进行修改,
//只有对map进行添加或着删除的时候才会影响到:
for(iter=map_struct.begin();iter!=map_struct.end();iter++)
cout<<iter->first<<"\t"<<(iter->second)->i<<"\t"<<(iter->second)->a<<endl;
return 0;
}
因为在多线程的编程时候,可能同时有多个线程要使用、修改map我给它加个锁,在比如删除、插入、map.find()、map.size()等操作时要锁住后在操作,一旦将变量添加到map中比如我的struct CTest ct 的变量对它进行操作的时候,可能还需要一个内部锁防止多个线程对其操作,
我问在我对struct CTest ct的变量修改的时候我在锁住内部锁之前是不是还得锁住维护map的锁哪?
头像
BigSnake.NET
帖子: 12522
注册时间: 2006-07-02 11:16
来自: 廣州
联系:

Re: 再问STL中map的使用

#2

帖子 BigSnake.NET » 2008-11-26 17:36

这个肯定是可以的, map 不可能知道你外面还有个指针指着 ct
map::operator [] 返回的也是值类型的引用,可以当左值用

PS: 内存泄漏了
^_^ ~~~
要理解递归,首先要理解递归。

地球人都知道,理论上,理论跟实际是没有差别的,但实际上,理论跟实际的差别是相当大滴。
Wayne.viichi
帖子: 8
注册时间: 2008-11-16 20:13

Re: 再问STL中map的使用

#3

帖子 Wayne.viichi » 2008-11-26 23:38

LZ对map的理解不够深刻,所以才会有“惊动map”的想法。

map仅仅是一个容器,也就是说 它更本不关心你放了什么进去。

map的实现 完全可以理解为 一个有序链表,即:list + index (表+索引)的结构。

而map对index的默认实现,仅仅是有序表的操作。

对于多线程应用程序的实现,程序结构设计非常重要。其重点在于 “临界区资源” 的访问。

程序员需要 考虑 那些是临界区资源 那些操作需要互斥,那些数据需要同步。

容器的使用 仅仅是数据的组织方式 二者不是一个范畴。
头像
xhy
帖子: 3916
注册时间: 2005-12-28 1:16
系统: Ubuntu 12.10 X64
来自: 火星

Re: 再问STL中map的使用

#4

帖子 xhy » 2008-11-26 23:51

STL的Map实现是红黑树或者平衡二叉树

容器的操作是不是thread-safe 得看实现

标准C++里没有线程这个概念,也不支持多线程

j建议假定STL不是线程安全 自己加锁比较保险
目前负债150多万
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: 再问STL中map的使用

#5

帖子 weihua2008 » 2008-11-27 14:28

Wayne.viichi,
你说我开启多个线程的话是不是应该把map看成是“临界区资源”哪?
多个线程同时使用这个map的啊,是不是在map.erase()时候因该加锁
还有我在调用map.size()是不是同样也得考虑锁住map再使用?
Wayne.viichi
帖子: 8
注册时间: 2008-11-16 20:13

Re: 再问STL中map的使用

#6

帖子 Wayne.viichi » 2008-11-27 22:19

我的通常做法是:
class CCriticalData
{
public:
bool Init();
void Clear();

void Lock();
void Unlock();

//your data..
int A;
map<int, int> myMap;
};

CCriticalData cd;
cd.Init();

//....
// the one thread.
cd.Lock();
cd.A = 20;
cl.myMap.insert(...);
//...
cl.Unlock();

// the others thread.
cd.Lock();
cd.A += 20;
cd.myMap.find(10);
//...
cd.Unlock();

在一般应用中,容器本身的线程安全是没有实际意义的。因为它无法保证你元素数据的线程安全。
临界区的应用在于,确定每个临界区的范围(其中包含的数据),使其交集最小。
这里的交集是指同时进入临界区的交集。比如 临界区A中包含a,B包含b,C包含c,同时操作a,b或c就产生了临界区交集。
交集过多,则可能导致死锁。临界区数量过少导致性能低下。
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: 再问STL中map的使用

#7

帖子 weihua2008 » 2008-11-28 9:45

Wayne.viichi,
你说的额容器本省的线程安全是没有实际意义的我不是很赞同
难道可以允许多个线程同时对一个map进行insert,find,erase操作吗?不是吧
我看了你的实例,每次你对
map的操作钱都加了锁,而在操作完成后解锁,当然在锁住的同时不光对map的insert。erase操作,还有对数据的操作
我也直到你加锁不加锁,对于存放的数据本身的安全性起不到实际的保护作用,
但是单纯的说对于同一个map的insert和erase操作是不是应该加锁,以免同时有多个线程对map进行操作?
'我说的是不是阿?
Wayne.viichi
帖子: 8
注册时间: 2008-11-16 20:13

Re: 再问STL中map的使用

#8

帖子 Wayne.viichi » 2008-11-28 23:44

其实在实际项目开发中,单纯的使用容器是不可能的..

呵呵 很高兴 和你探讨这些..
加我的msn:wayne_raindot@hotmail.com
或者QQ:38814166
头像
xhy
帖子: 3916
注册时间: 2005-12-28 1:16
系统: Ubuntu 12.10 X64
来自: 火星

Re: 再问STL中map的使用

#9

帖子 xhy » 2008-11-29 0:03

weihua2008 写了:Wayne.viichi,
你说的额容器本省的线程安全是没有实际意义的我不是很赞同
难道可以允许多个线程同时对一个map进行insert,find,erase操作吗?不是吧
我看了你的实例,每次你对
map的操作钱都加了锁,而在操作完成后解锁,当然在锁住的同时不光对map的insert。erase操作,还有对数据的操作
我也直到你加锁不加锁,对于存放的数据本身的安全性起不到实际的保护作用,
但是单纯的说对于同一个map的insert和erase操作是不是应该加锁,以免同时有多个线程对map进行操作?
'我说的是不是阿?
锁会导致性能低下,实际工作中线程很少需要读写同一套数据,线程是用来解决io等待的。

尽量减少线程的使用,尽量减小锁的粒度。
目前负债150多万
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: 再问STL中map的使用

#10

帖子 weihua2008 » 2008-11-29 9:07

xhy,
说的有道理,但是我至少要两个线程才能完成功能,而且在这两个线程中都有可能对map进行insert和erase操作
那么加锁是不是必须的,没有锁的话就不能保证操作的正确性了不是
就是在这两个线程中用锁我都上愁,下面是一个很伤脑筋的例子
bool CTest::DeleteFromMap(int nSocketFd)
{
map<int, tagServSockContext*>::iterator iter;
pthread_mutex_lock(&(m_MapLock));
iter=m_Map.find(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
if(iter!=m_tMap.end())
{ //从m_Map中将相关信息删除
pthread_mutex_lock(&(m_MapLock));
int i= m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
if(i==1)
{
//关闭文件描述符
close(nSocketFd);
FreetagServSockContext(ptagServSockContext);//释放内存包括消毁内部锁
return true;
}
else//i==0
{
cout<<"m_Map中删除相关信息失败\n";
return false;
}

}
else//if(iter==m_map.end())
{
cout<<"在m_map中没有对象可删除\n";
return false;
}
}
还是这样做好哪?
bool CTest::DeleteFromMap(int nSocketFd)
{
map<int, tagServSockContext*>::iterator iter;
pthread_mutex_lock(&(m_MapLock));
iter=m_Map.find(nSocketFd);

if(iter!=m_Map.end())
{ //从m_Map中将相关信息删除
int i= m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
if(i==1)
{
//关闭文件描述符
close(nSocketFd);
FreetagServSockContext(ptagServSockContext);//释放内存包括消毁内部锁
return true;
}
else//i==0
{
cout<<"m_Map中删除相关信息失败\n";
return false;
}

}
else//iter==m_map.end()
{ pthread_mutex_unlock(&(m_MapLock));
cout<<"在m_map中没有对象可删除\n";
return false;
}
}
说说大家的看法...............
Wayne.viichi
帖子: 8
注册时间: 2008-11-16 20:13

Re: 再问STL中map的使用

#11

帖子 Wayne.viichi » 2008-11-29 12:44

xhy 写了:
weihua2008 写了:Wayne.viichi,
你说的额容器本省的线程安全是没有实际意义的我不是很赞同
难道可以允许多个线程同时对一个map进行insert,find,erase操作吗?不是吧
我看了你的实例,每次你对
map的操作钱都加了锁,而在操作完成后解锁,当然在锁住的同时不光对map的insert。erase操作,还有对数据的操作
我也直到你加锁不加锁,对于存放的数据本身的安全性起不到实际的保护作用,
但是单纯的说对于同一个map的insert和erase操作是不是应该加锁,以免同时有多个线程对map进行操作?
'我说的是不是阿?
锁会导致性能低下,实际工作中线程很少需要读写同一套数据,线程是用来解决io等待的。

尽量减少线程的使用,尽量减小锁的粒度。
呵呵,我是一个Win32程序员,linux是一个新手..
在win32下开发服务器程序,通常使用完成端口,这样操作数据的线程是完全不确定的..
在linux下 可能我的这个想法好改一改 呵呵..
在多核时代,多线程的使用可以大幅度提高程序运行效率,但线程安全也随之变成了首要的问题。

刚刚查到.. linux下与Win32完成端口功能相似的接口,epoll。
大家以后多多交流..
头像
xhy
帖子: 3916
注册时间: 2005-12-28 1:16
系统: Ubuntu 12.10 X64
来自: 火星

Re: 再问STL中map的使用

#12

帖子 xhy » 2008-11-29 15:50

Wayne.viichi 写了:
xhy 写了:
weihua2008 写了:Wayne.viichi,
你说的额容器本省的线程安全是没有实际意义的我不是很赞同
难道可以允许多个线程同时对一个map进行insert,find,erase操作吗?不是吧
我看了你的实例,每次你对
map的操作钱都加了锁,而在操作完成后解锁,当然在锁住的同时不光对map的insert。erase操作,还有对数据的操作
我也直到你加锁不加锁,对于存放的数据本身的安全性起不到实际的保护作用,
但是单纯的说对于同一个map的insert和erase操作是不是应该加锁,以免同时有多个线程对map进行操作?
'我说的是不是阿?
锁会导致性能低下,实际工作中线程很少需要读写同一套数据,线程是用来解决io等待的。

尽量减少线程的使用,尽量减小锁的粒度。
呵呵,我是一个Win32程序员,linux是一个新手..
在win32下开发服务器程序,通常使用完成端口,这样操作数据的线程是完全不确定的..
在linux下 可能我的这个想法好改一改 呵呵..
在多核时代,多线程的使用可以大幅度提高程序运行效率,但线程安全也随之变成了首要的问题。

刚刚查到.. linux下与Win32完成端口功能相似的接口,epoll。
大家以后多多交流..
epoll是最近几年才有的,以前都是那该死的poll和select,性能低下。
有评测数据,epoll和kqueue的性能不相上下,非常彪悍。
更方便的办法是用libevent这个库,它封装了常见的事件通知机制。

多线程能不能提高程序的性能很难说,如果问题本身就具有原子性,不能分割,多线程就没有意义。

linux和unix传统倾向于多进程的使用,而非多线程。
目前负债150多万
头像
xhy
帖子: 3916
注册时间: 2005-12-28 1:16
系统: Ubuntu 12.10 X64
来自: 火星

Re: 再问STL中map的使用

#13

帖子 xhy » 2008-11-29 16:21

代码: 全选

pthread_mutex_lock(&(m_MapLock));
m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
这样就行了,不需要判断是否存在
目前负债150多万
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: 再问STL中map的使用

#14

帖子 weihua2008 » 2008-11-29 19:48

xhy,
单纯的对于一个删除map中数据的操作,可能我做的有点复杂,
我还是没有把问题说明白,或者说我的的疑问所在,我的疑问可能就是 你说道尽量减少锁的粒度,
你抛开我的程序的不提,你说使用锁的时候更倾向于第一种使用方法还是第二种使用方法
第一种:尽量减少锁的粒度,就是哪怕我在一个函数中(多锁住几次多释放几次)也不会(少锁住几次,叫每次锁住的时间长一点),
第二种:在一个函数中比较接近的操作我只锁住一次锁,把一个或者两个操作完成后在释放锁,

你说是加锁解锁锁用的时间长还是,程序运作的时间长?
1.pthread_mutex_lock(&(m_MapLock));
iter=m_Map.find(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
if(iter!=m_tMap.end())
{ //从m_Map中将相关信息删除
pthread_mutex_lock(&(m_MapLock));
int i= m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
//////////////////////////////////////////////////////////////////////////
2.pthread_mutex_lock(&(m_MapLock));
iter=m_Map.find(nSocketFd);

if(iter!=m_Map.end())
{ //从m_Map中将相关信息删除
int i= m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
这两种方式各位喜欢哪种,不能说喜欢,应该说按道理来说应该选择哪种方式?(仅从逻辑和效率上考虑两者的舍取)
、、、、、、、、、、、、、、、、。。。。
其实我觉得我程序的第一种方式就不对,或者说就不严谨,
你比如说我先锁住map,判断一下map中有没有要找的东西a,我就放开了,接着又一个线程又锁住了map也同样的判断map中有没有要找的这个东西a,第二个线程也放开了锁,第一个线程又锁住了锁,该线程在第一次解锁后判断了一下函数的返回值一看有要找的东西a,在第二次锁住后把找到的数据a删除了,第二个线程在第一次解锁后分析函数的返回值也同样得出map有要找的东西a的论断,结果他在第二次锁住map后也会调用删除数据a的函数进行删除,其实此时数据a已经被第一个线程删除了,这是我的遐想,把事情想的太坏了,但我认为这个问题的发生是有可能的,所以应该是锁住map后判断有没有要找的数据,有的话删除以后再解锁,没有的话就直接解锁
我说明白了吗,
还是希望智者帮我分析一下锁的使用原则,
头像
xhy
帖子: 3916
注册时间: 2005-12-28 1:16
系统: Ubuntu 12.10 X64
来自: 火星

Re: 再问STL中map的使用

#15

帖子 xhy » 2008-11-29 21:15

map是不需要判断元素是否存在的,可以直接删除

代码: 全选

#include <iostream>
#include <map>

int main()
{
    std::map <int, int> a;
    a[1] = 1000;
    a[2] = 2000;
    std::cout<<a[1]<<std::endl;
    std::cout<<a[2]<<std::endl;
    a.erase(1);
    a.erase(3);
    std::cout<<a[1]<<std::endl;
    std::cout<<a[2]<<std::endl;
}
虽然map里面a[3]并不存在,但是去erase它也不会有什么副作用。

所以你只需要

代码: 全选

pthread_mutex_lock(&(m_MapLock));
m_Map.erase(nSocketFd);
pthread_mutex_unlock(&(m_MapLock));
就可以了


简而言之,锁用的越少越好,每次锁住的时间越短越好。
目前负债150多万
回复