【问题标题】:Access pthread shared std:map without data race在没有数据竞争的情况下访问 pthread 共享 std:map
【发布时间】:2015-12-03 08:26:50
【问题描述】:

我的方案是有一个主线程和数十个工作线程。工作线程将处理来自不同端口的传入消息。

我想要做的是让主线程和工作线程共享同一个映射,工作线程将数据保存到映射中(在不同的存储桶中)。并且主线程会定期对地图内容进行grep。

代码如下:

struct cStruct
{
    std::map<string::string> map1; 
    pthread_mutex_t mutex1;
    pthread_mutex_t mutex2;  
};

int main(){

    struct cStruct cStruct1;   
    while (condition){
        pthread_t th;
        int th_rc=pthread_create(&th,NULL,&task,(void *) &cStruct1);
    }

}

void* task(void* arg){
    struct cStruct cs = * (struct cStruct*) arg;

    while (coming data){
        if (main_thread_work){
            pthread_cond_wait(&count_cond, &cs.mutex1)
        }  

        pthread_mutex_lock(&cs.mutex1);
        // add a new bucket to the map
        cs.map1(thread_identifier)=processed_value;
        pthread_mutex_unlock(&cs.mutex1);

    }

void* main_thread_task(void* arg){

    sleep(sleep_time);
    main_thread_work = true;
    pthread_mutex_lock(&cs.mutex1);
    // main_thread reads the std::map
    main_thread_work = false;
    pthread_cond_broadcast(&count_cond, &cs.mutex1)
    pthread_mutex_unlock(&cs.mutex1);    
}

我的问题是:

为了改变地图大小,我应该使用锁来保护地图。 但是对于具有特定键更新的地图,我可以让不同的线程同时修改地图吗? (假设不会同时访问两个相同的地图桶)

对于主线程 greps 地图,我想在主线程 grep 地图内容时使用条件等待来保持所有工作线程,然后执行 pthread_cond_broadcast 以唤醒然后。问题是如果一个工作线程在 main 开始工作时更新 map,就会出现数据竞争。

请分享一些想法来帮助我改进我的设计。

编辑 1: 添加 main_thread_task()。 我要避免的是工作线程在“pthread_cond_broadcast”之后到达 pthread_cond_wait 并且逻辑出错了。

所以我在主线程广播工作线程之前错误的 main_thread_work。

【问题讨论】:

  • 在结构中使用互斥锁来保护它是错误的(您需要在结构周围使用互斥锁)。
  • 你的意思是我不应该在结构中包含锁吗? (我应该声明一个全局锁吗?)
  • 不是全局的,而是在外部“范围”中
  • 但是对于pthread_create我只能传入一个参数,除了使用结构体或全局变量还有什么办法吗?
  • @tester 不要使用线程创建来告诉线程该做什么。创建线程后告诉线程要做什么。这样就容易多了,你可以将任何你喜欢的东西传递给线程。如果您坚持在创建线程时执行此操作,请动态分配一个包含线程所需所有内容的结构,将指向该结构的指针传递给线程,并在完成后让线程删除该结构。

标签: c++ multithreading locking pthreads


【解决方案1】:
while (coming data){
    if (main_thread_work){
        pthread_cond_wait(&count_cond, &cs.mutex1)
    }  

    pthread_mutex_lock(&cs.mutex1);

这显然是不对的。除非您持有保护它的锁,否则您无法检查main_thread_work。对pthread_cond_wait 的调用如何释放它不持有的锁?!

这应该是这样的:

void* task(void* arg){
    struct cStruct cs = * (struct cStruct*) arg;

    // Acquire the lock so we can check shared state
    pthread_mutex_lock(&cs.mutex1);

    // Wait until the shared state is what we need it to be
    while (main_thread_work)
        pthread_cond_wait(&count_cond, &cs.mutex1)

    // Do whatever it is we're supposed to do when the
    // shared state is in this state
    cs.map1(thread_identifier)=processed_value;

    // release the lock
    pthread_mutex_unlock(&cs.mutex1);
}

【讨论】:

  • 另一个线程运行 main_thread_task() 控制 main_thread_work 并持有互斥锁。我看到 pthread_cond_wait() 示例必须使用锁来保护它。但对我来说,我需要让“多个”工作线程停在这里,直到线程运行 main_thread_task() 进行广播。不确定这会导致问题。请分享您对此的建议。谢谢!
  • @tester 好的。所以呢? (你明白pthread_cond_wait 是一个无条件的、原子的“解锁并等待”操作吗?)
  • 我是 pthread 的新手,但“解锁并等待”对我来说真的很好!那么 pthread_cond_wait 保护 pthread_mutex_lock 和 pthread_cond_wait 之间的区域呢?另一个问题是:如果我在pthread_mutex_lock和pthread_cond_wait之间没有任何关系,是否需要在pthread_cond_wait之前添加pthread_mutex_lock?
  • @tester 这是必要的,否则你怎么知道你需要等待?你等待的事情可能已经发生了。互斥锁保护共享状态。总是lock(); while (state_is_wrong) wait(); do_stuff(); unlock();
  • “你等待的事情可能已经发生了”。这是否意味着 pthred_cond_broadcast 发生在 pthread_cond_wait 之前?在这种情况下,等待线程不会被唤醒。但是我要避免的一件事是,如果工作线程以非常“高频率”运行,它们可能会相互锁定并减慢速度。在我的 main_thread_task 函数中,我在 pthred_cond_broadcast 之前交换了标志 main_thread_work,并尝试使用此标志来保护 pthred_cond_wait。
【解决方案2】:

您应该在每次访问地图时使用mutex 锁定机制(在您的情况下),而不仅仅是添加新的“存储桶”。如果 T1 尝试在 T2 插入新存储桶时向映射中写入一些值,则 T1 使用的指针/迭代器将变得无效。

关于pthread_cond_wait。如果其他线程所做的唯一事情就是修改地图,它可能会完成这项工作。如果他们执行其他计算或处理一些非共享数据,最好使用相同的mutex 来保护对地图的访问,并让其他线程完成他们的工作,此时可能与共享地图无关。

【讨论】:

  • @tester 我刚刚编辑了关于pthread_cond_wait的答案。
  • 谢谢。作为您的建议,我在 main_thread 中添加了互斥保护。 pthred_cond_wait 的常见使用示例通常在 pthred_mutex_lock 之前和 pthred_mutex_unlock 之后。但是我没有使用这两个锁,因为我需要工作线程都等待并且在 main 广播时全部唤醒。这个实现有什么副作用吗?
  • @tester 我现在又看了一遍,我想这次我明白你的意思了。所以是的,你需要用main_thread_work 的条件检查来包装pthread_cond_wait(&amp;count_cond, &amp;cs.mutex1),否则你会在它上面竞争(main_thread_work),因为主线程可以随时更新它,以便工作线程将得到一个过时的值。
猜你喜欢
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-31
  • 2013-06-19
相关资源
最近更新 更多