Chinaunix

标题: std::map并发时,只在写入的时候加锁,读到时候不加锁会有问题么? [打印本页]

作者: tesing2009    时间: 2013-10-24 14:22
标题: std::map并发时,只在写入的时候加锁,读到时候不加锁会有问题么?
Hi,

  小弟用到了std::map,但是为了提高多线程并发性,
  
  不想加太多的锁,只在写入的时候加锁,读取的时候不加锁,

  这样每次写入的进程只有一个,读取的进程有多个,在读取的时候会有问题么?

作者: folklore    时间: 2013-10-24 14:25
有。。。。。。
作者: linux_c_py_php    时间: 2013-10-24 14:26
只要有并发读写就会有问题, 这个很容易理解吧.
作者: 群雄逐鹿中原    时间: 2013-10-24 14:34
读的时候,正在写,肯定有问题
作者: bruceteen    时间: 2013-10-24 14:38
BTW:C++11 有没有提供读写锁?
作者: folklore    时间: 2013-10-24 14:45
回复 5# bruceteen


    c++ 不太可能提供一般意义上的锁及多线程支持, 原因你懂得~~
相对来说, 脚本语言以及像Java这样有VM的语言则对此毫无压力~~
作者: bruceteen    时间: 2013-10-24 14:49
回复 6# folklore
但 C++11 提供了多线程和锁的支持呀,我在mingw(gcc4.8.1)上用过
   
作者: tesing2009    时间: 2013-10-24 15:04
回复 3# linux_c_py_php


    是的,但是现在业务处理的时间极短,每次都加锁则影响很大。大神,有啥好方法可以这次的加锁么。。多谢~
作者: linux_c_py_php    时间: 2013-10-24 15:34
tesing2009 发表于 2013-10-24 15:04
回复 3# linux_c_py_php


方法当然有啊, 弄10个map, 每个map一把锁, 按照hash(key) % 10后选中对应map加锁操作.
作者: tesing2009    时间: 2013-10-24 15:38
回复 9# linux_c_py_php


    是的,这时候可以增加10倍并发,但是我想把单个的map的锁也去掉。。。
作者: tesing2009    时间: 2013-10-24 15:40
回复 6# folklore


    对于读写锁,写锁会阻塞后面的所有读么?
  
    否则会因为读并发太多,读锁一直被占用,导致写锁永远获取不到的情况
作者: folklore    时间: 2013-10-24 16:55
回复 7# bruceteen

你是说parallel_for?
    这种所谓的锁只是表面上的支持, 如果你要这么说, VC也“支持”。。。

锁和线程严重依赖于实现平台, 无法做为语言元素加入标准库的。。。
除非给库分等级,也就是说某个平台支持到某级的库为止。
作者: folklore    时间: 2013-10-24 16:55
回复 11# tesing2009


    会。。。。
作者: huangyacong    时间: 2013-10-24 16:59
俺记得在哪里看到vc的STL库写着线程安全的,可是,是真的吗?别被骗了,微软这个大坏蛋
作者: folklore    时间: 2013-10-24 17:00
如果map某个锁成为性能瓶颈,
你只能自已实现数据结构,
尽量减小锁的粒度和占有时间。

减小占有时间的例子:
  1. lock();
  2. deleteitem(removeitem());
  3. release();
复制代码
改为:
  1. lock();
  2. item it =removeitem())
  3. release();
  4. deleteitem(it);
复制代码

作者: csumck    时间: 2013-10-24 17:09
回复 10# tesing2009


    可以想办法避免并发访问map。 不然不可能避免脏数据。
作者: tesing2009    时间: 2013-10-24 18:24
回复 15# folklore


    好的,多谢回答 :)
作者: tesing2009    时间: 2013-10-24 19:12
回复 15# folklore
  1.     lock();
  2.     map<>::iterator itor = mpValue.find(szKey);
  3.     unlock();
  4.    //线程B写入mapValue
  5.    //线程A操作itor
复制代码
还有个问题请教下,如上代码,线程A在获取map的itor后,线程B对这个map进行写入,这时候线程A再访问这个itor,这个itor有可能失效么?
    也就是说,加锁是应该包含线程A整个访问itor的的周期么?
   
   虽然一般不会这样用,通常map直接保存key和对应的数据对象指针,然后直接操作这个数据对象了。没有深入到map里面看,不了解诶。。
   


作者: folklore    时间: 2013-10-24 19:48
回复 18# tesing2009


    会失效, Map不支持访问计数。
但可以使用 std::SharedPtr做为Map元素来回避此问题。
作者: tesing2009    时间: 2013-10-24 21:38
回复 19# folklore


    好的,多谢,我得多看点代码了。。
作者: bruceteen    时间: 2013-10-25 08:33
回复 12# folklore
不是 OpenMP、TBB、CUDA、OpenCL、MPI 等东西,C++11标准真的加入了线程支持。
我写个事例代码给你睇下,在MinGW(gcc4.8.1)上能编译运行
  1. #include <iostream>
  2. #include <thread>
  3. #include <mutex>
  4. using namespace std;

  5. void foo( std::mutex& m )
  6. {
  7.     //std::this_thread::sleep_for (chrono::seconds(1));

  8.     for( size_t i=0; i!=10; ++i )
  9.     {
  10.         std::lock_guard<std::mutex> lock(m);
  11.         cout <<  "ThreadID(" << std::this_thread::get_id() << "): " << i << endl;
  12.     }
  13. }

  14. int main()
  15. {
  16.     std::mutex m;

  17.     thread t1( &foo, std::ref(m) );
  18.     thread t2( &foo, std::ref(m) );

  19.     t1.join();
  20.     t2.join();

  21.     return 0;
  22. }
复制代码
一种可能的输出为
  1. ThreadID(2): 0
  2. ThreadID(3): 0
  3. ThreadID(2): 1
  4. ThreadID(3): 1
  5. ThreadID(2): 2
  6. ThreadID(3): 2
  7. ThreadID(2): 3
  8. ThreadID(3): 3
  9. ThreadID(2): 4
  10. ThreadID(3): 4
  11. ThreadID(2): 5
  12. ThreadID(3): 5
  13. ThreadID(2): 6
  14. ThreadID(3): 6
  15. ThreadID(2): 7
  16. ThreadID(3): 7
  17. ThreadID(2): 8
  18. ThreadID(3): 8
  19. ThreadID(2): 9
  20. ThreadID(3): 9
复制代码

作者: windoze    时间: 2013-10-25 10:45
看这篇 http://erdani.com/publications/cuj-2004-12.pdf
读懂,然后自己实现一个。
作者: folklore    时间: 2013-10-26 11:56
回复 21# bruceteen


    好, 我去试试看。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2