简体   繁体   English

我应该总是在多线程编程中锁定全局数据吗,为什么或为什么不呢?

[英]should i always lock the global data in multi-thread programming, why or why not?

I'm new to multi-thread programming(actually, i'm not a fresh man in multi-threading, but i always use global data for reading and writing thread, i think it makes my code ugly and slow, i'm eager to improve my skill) 我是多线程编程的新手(实际上,我不是多线程编程的新手,但是我总是使用全局数据来读写线程,我认为这会使我的代码难看又慢,我很渴望以提高我的技能)

and i'm now developing a forwarder server using c++, for simplify the question, we suppose there are only two threads, a receiving-thread and a sending-thread, and, the stupid design as usual, I have an global std::list for saving data :( 而且我现在正在使用c ++开发转发器服务器,为简化问题,我们假设只有两个线程,一个接收线程和一个发送线程,并且像往常一样愚蠢的设计中,我有一个全局std ::保存数据列表:(

receiving-thread read raw data from server and wirte it into global std::list. receiving-thread从服务器读取原始数据,并将其写入全局std :: list。

sending-thread read global std::list and send it to several clients. sending-thread读取全局std :: list并将其发送给多个客户端。

i use pthread_mutex_lock to sync the global std::list. 我使用pthread_mutex_lock来同步全局std :: list。

the problem is that the performance of forward server is poor, global list locked when receiving-thread is wrting, but at that time, my sending-thread wanna read, so it must waiting, but i think this waiting is useless. 问题是转发服务器的性能很差, receiving-thread写时全局列表被锁定,但是那时我的sending-thread想读取,因此它必须等待,但是我认为这种等待是没有用的。

what should i do, i know that global is bad, but, without global, how can i sync these two threads? 我应该怎么做,我知道全局是不好的,但是如果没有全局,我该如何同步这两个线程?

i'll keep searching from SO and google. 我将继续从SO和Google搜索。

any suggestions, guides, technology or books will be appreciated. 任何建议,指南,技术或书籍将不胜感激。 thanks! 谢谢!

EDIT 编辑

  1. for any suggestions, i wanna know why or why not, please give me the reason, thanks a lot. 对于任何建议,我想知道为什么或为什么不这样做,请给我原因,非常感谢。

Notes: 笔记:

  1. Please provide more complete examples: http://sscce.org/ 请提供更完整的示例: http : //sscce.org/

Answers: 答案:

  1. Yes, you should synchronize access to shared data. 是的,您应该同步访问共享数据。

    • NOTE: this makes assumptions about std::list implementation - which may or may not apply to your case - but since this assumptions is valid for some implementation you cannot assume your implementation must be thread safe without some explicit guarantee 注意:这对std :: list实现进行了假设-可能适用于您的情况,或者可能不适用于您的情况-但由于此假设对某些实现有效,因此您不能假设没有某些明确的保证,您的实现必须是线程安全的
    • Consider the snippet: 请考虑以下代码段:

       std::list g_list; void thread1() { while( /*input ok*/ ) { /*read input*/ g_list.push_back( /*something*/ ); } } void thread2() { while( /*something*/ ) { /*pop from list*/ data x = g_list.front(); g_list.pop_front(); } } 
    • say for example list has 1 element in it 例如说列表中有1个元素
    • std::list::push_back() must do: std :: list :: push_back()必须执行以下操作:
      • allocate space (many CPU instructions) 分配空间(许多CPU指令)
      • copy data into new space (many CPU instructions) 将数据复制到新空间(许多CPU指令)
      • update previous element (if it exists) to point to new element 更新前一个元素(如果存在)以指向新元素
      • set std::list::_size 设置std :: list :: _ size
    • std::list::pop_front() must do: std :: list :: pop_front()必须执行以下操作:
      • free space 可用空间
      • update next element to not have previous element 更新下一个元素以使其不具有上一个元素
      • set std::list_size 设置std :: list_size
    • Now say thread 1 calls push_back() - after checking that there is an element (check on size) - it continues to update this element - but right after this - before it gets a chance to update the element - thread 2 could be running pop_front - and be busy freeing the memory for the first element - which could result then in thread 1 causing a segmentation fault - or even memory corruption. 现在说线程1调用push_back()-在检查是否存在一个元素(检查大小)之后-继续更新该元素-但是紧接着-在它有机会更新该元素之前-线程2可能正在运行pop_front -并忙于为第一个元素释放内存-这可能会导致线程1导致分段错误-甚至是内存损坏。 Similarly updates to size could result in push_back winning over pop_front's update - and then you have size 2 when you only have 1 element. 类似地,大小的更新可能导致push_back胜过pop_front的更新-然后,当您只有1个元素时,大小为2。
  2. Do not use pthread_* in C++ unless you really know what your doing - use std::thread (c++11) or boost::thread - or wrap pthread_* in a class by yourself - because if you don't consider exceptions you will end up with deadlocks 除非您真的知道自己在做什么,否则不要在C ++中使用pthread_ *-使用std :: thread(c ++ 11)或boost :: thread-或将pthread_ *自己包装在类中-因为如果您不考虑异常你最终会陷入僵局

  3. You cannot get past some form of synchronization in this specific example - but you could optimize synchronization 在此特定示例中,您无法摆脱某种形式的同步-但您可以优化同步

    1. Don't copy the data itself into and out of the std::list - copy a pointer to the data into and out of the list 不要将数据本身复制到std :: list中或从中复制出来-将指向数据的指针复制到列表中或从列表中复制出来
    2. Only lock while your actually accessing the std::list - but don't make this mistake: 仅在实际访问std :: list时锁定-但不要犯此错误:

       { // lock size_t i = g_list.size(); // unlock if ( i ) { // lock // work with g_list ... // unlock } } 
  4. A more appropriate pattern here would be a message queue - you can implement one with a mutex, a list and a condition variable. 这里更合适的模式是消息队列-您可以使用互斥锁,列表和条件变量来实现。 Here are some implementations you can look at: 您可以查看以下一些实现:

  5. There is also the option of atomic containers, look at: 还有原子容器的选项,请看:

    • http://calumgrant.net/atomic/ - not sure if this is backed by actual atomic storage (as opposed to just using synchronization behind an interface) http://calumgrant.net/atomic/-不确定是否由实际的原子存储支持(与仅在接口后面使用同步相反)
    • google for more 谷歌更多
  6. You could also go for an asynchronous approach with boost::asio - though your case should be quite fast if done right. 您还可以采用boost :: asio的异步方法-尽管如果正确的话,您的案例应该很快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM