最近计网课设选择了写一个ftp服务器,其中我想把服务器端写成多线程部分。由此接触了posix线程及其相关部分。相关阅读书籍:【1】现代操作系统 【2】posix多线程程序设计。

  关于第二本是02年的,很久没出过新版了,NTPL与17年前的接口仍然大多一致,甚至是一样,由此引发了一些思考:现在那些花里胡哨的技术更新太快了,比如说java,c#,甚至c++每隔两年就要发布一个新的标准,然而这些底层接口仍然不变,学好了这些基础是不是更棒呢。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

 

  步入正题:我最初想的线程池是弹出式的:也就是有一个请求到来,进程就为该请求创建一个线程。代码如下:

class sample
{
public:
    void process();
};

template <typename T>
class threadPool
{
public:
    threadPool();
    void append(T &s);
    static void *worker(void *s);
    ~threadPool()
    {
        if(ifDestroy==false)
        {
            destroy();
        }
    }

    void destroy()
    {
        pthread_mutex_destroy(&t);
        destroy=true;
    }

private:
    pthread_t threads[3];
    bool run = true;
    bool ifDestroy=false;
};

template <typename T>
void threadPool<T>::append(T& s)
{
    pthread_t t;
    pthread_create(&t,nullptr,worker,&s);
    pthread_detach(t);
 }

template <typename T>
threadPool<T>::threadPool()
{

} template <typename T> void *threadPool<T>::worker(void *s) { auto work= static_cast<sample *>(s); work->process(); return nullptr; } #endif //!THREAD_POOL_H

  这里我把请求的地址作为参数传入线程,并将之设置为分离的(detached)。

  关于线程的start_routine成员函数必须是静态的,这个还不清楚为什么,难道是跟c++的多态特性不兼容吗?

  这个的缺点是显而易见的:若任务很小,只需要不多的指令,那么频繁的创建和回收销毁线程的开销则是巨大的。由此引入了我的线程池的下一个版本:

  

  这回使用了mutex(mutual exclusion)互斥锁

class sample
{
public:
    void process();
};

template <typename T>
class threadPool
{
public:
    threadPool();
    void append(T &s);
    static void *worker(void *s);
    ~threadPool()
    {
        if(ifDestroy==false)
        {
            destroy();
        }
    }

    void destroy()
    {
        pthread_mutex_destroy(&t);
        destroy=true;
    }

private:
    std::queue<T *> tasks;
    pthread_t threads[3];
    bool run = true;
    pthread_mutex_t t;
    bool ifDestroy=false;
};

// template <typename T>
// void threadPool<T>::append(T& s)
// {
//     pthread_t t;
//     pthread_create(&t,nullptr,worker,&s);
//     pthread_detach(t);
// }
template <typename T>
threadPool<T>::threadPool() : tasks()
{
    pthread_mutex_init(&t, nullptr);
    for (int i = 0; i < 3; ++i)
    {
        pthread_create(&threads[i], nullptr, worker, this);
        pthread_detach(threads[i]);
    }
}

template <typename T>
void threadPool<T>::append(T &s)
{
    pthread_mutex_lock(&t);
    tasks.push(static_cast<T *>(&s));
    printf("%u append.\n", pthread_self());
    pthread_mutex_unlock(&t);
}

template <typename T>
void *threadPool<T>::worker(void *s)
{
    auto pool = static_cast<threadPool<T> *>(s);
    while (pool->run)
    {
        pthread_mutex_lock(&(pool->t));
        if(pool->tasks.empty())
        {
            pthread_mutex_unlock(&(pool->t));
       pthread_yeild();
continue; } T *work = pool->tasks.front(); pool->tasks.pop(); pthread_mutex_unlock(&(pool->t)); work->process(); } return nullptr; } #endif //!THREAD_POOL_H

  这个线程池用一个队列来先存储请求,用mutex保证其原子性(atomic),串行性。【2】的p:52说道:“互斥量的本质是在串行执行”。因为还不想涉及到条件变量和信号量,所以这里当工作线程检测到队列为空时就pthread_yield()暂时放弃cpu资源给其他线程,然而这样的不足仍有很多:比如当队列为空时,工作线程就会频繁地加锁解锁损耗计算机。【2】的p:52说到:”互斥量不是免费的。需要时间来加锁解锁。“

  

  关于信号量和条件变量,且听下回分解。

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄