线程池的坑

最近实现线程池踩了不少坑…记录如下:

问题描述

线程池创建了两个线程,放两个任务进去,发现任务没执行.
例如:


void task()
{
    std::cout<<"hello world"<<std::endl;
}
void task2(int i)
{
    std::cout<<"hello world "<<i<<std::endl;
}
int main()
{
    threadpool p(10,2);//10 queuesize 2 threads

    p.start();
    p.addtask(task);
    p.addtask(boost::bind(task2,3));


    return 0;
}

程序运行之后没有任何打印在终端,但是gdb 和strace都是有输出的.

解决

一共发现两个问题最终解决之:
先看线程池的实现

threadpool::task threadpool::gettask()
{
    mutexlockguard lock(mutex_);
    while(queue_.empty()&&isstarted_)
    {
        notempty_.wait();
    }
    task t;
    if(!queue_.empty())//这里非常重要
    //之前我写的是  if(isstarted_)
    {
        assert(!queue_.empty());
        t=queue_.front();
        queue_.pop();
        notfull_.notify();
    }
    return t;
}

//这是线程执行体
//我们可以看到决定线程生命周期的是isstarted_这个变量
//当isstarted_变为false之后线程退出,pthread_join返回
void threadpool::runinthread()
{
    while(isstarted_)
    {

        task t(gettask());//will blocked until "add" ops
        if(t)
        {
            t();//run
        }
    }
}

这里解释了线程池的执行逻辑…我们有一个内部的方法gettask将从队列中取任务,如果线程还没启动或者没有任务,这个函数将阻塞…

到这里都没什么问题,因为我们会在线程池析构的时候,对等待的条件变量notify,并且将isstarted赋值为false,此时gettask()将返回一个空的task,循环将结束,pthread_join将顺利执行….

//析够函数
threadpool::~threadpool()
{
    if(isstarted_)
    {
    stop();
    }
}


void threadpool::stop()
{   
    {
    mutexlockguard lock(mutex_);
    isstarted_ = false;
    notempty_.notifyall(); //激活所有的线程

    }
    for_each(threads_.begin(),
        threads_.end(),
        boost::bind(&thread::join, _1));

}

一切都看起来那么的优雅美好,但是却忽略一个问题:
如果主线程执行得太快,那么很有可能工作线程还没有拿到任务,isstarted_就为false了 条件变量也唤醒了,线程拿到一个空任务,什么都不做就退出了,pthread_join也就返回了,线程没有做任何事情就退出了….

试着发现这一切发生的原因: isstarted_的赋值不是线程安全的….但是上面的代码明明是用mutexlock进行保护了?

wait调用自动释放锁,在主线程中add一个任务,notify线程,wait返回又会加上锁,析够函数不应该拿到锁修改临界区变量…

这个问题在https://github.com/chenshuo/muduo/issues/276

问题二

同样是线程没执行就退出了,这个比较简单,发现线程还没有start就被析够了,简单的sleep就能解决,不过sleep不能做同步原语,因为最好的解决办法还是用countdownlatch…

据我了解countdownlatch用法有两个:

1.每个任务进行countdown操作,初始化时count为任务数

countdownlatch latch(2);
void task()
{
    std::cout<<"hello world"<<std::endl;
    latch.countdown();
}
void task2(int i)
{
    std::cout<<"hello world "<<i<<std::endl;

    latch.countdown();
}
int main()
{
    threadpool p(10,2);//10 queuesize 2 threads

    p.start();

    p.addtask(task);
    p.addtask(boost::bind(task2,3));
    p.addtask(boost::bind(&countdownlatch::countdown,&latch));

    latch.wait();

    return 0;
}

2.count初始化为1,在程序结束前wait,左右类似于一个barrier

void task()
{
    std::cout<<"hello world"<<std::endl;
}
void task2(int i)
{
    std::cout<<"hello world "<<i<<std::endl;

}
int main()
{
    threadpool p(10,2);//10 queuesize 2 threads

    countdownlatch latch(1);
    p.start();

    p.addtask(task);
    p.addtask(boost::bind(task2,3));

    p.addtask(boost::bind(&countdownlatch::countdown,&latch));

    latch.wait();

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值