线程知识

线程

         包含了表示进程内执行环境必须的信息,其中包括进程中标识线程的线程ID,一组寄存器值,栈,调度优先级和策略,信号屏蔽字,errno变量以及线程私有数据。进程中所有信息对该进程的所有线程共享的,包括可执行的程序文本,程序的全局内存和堆内存,栈以及文件描述符。

多线程好处:为每种事件分配单独线程,简化处理异步事件的代码
                        可以访问相同的存储地址空间和文件描述符(多进程实现内存和文件描述符共享比较麻烦)

                        可以改变吞吐量,实现响应时间的改善

多线程编程模型适用范围观点:    1)单处理器也能得倒好处
                                                           2)多处理才能发挥出多线程的价值

           我只在单处理器上用过多线程,不了解多处理器,目前还没有自己的看法。

单线程服务器编程模型:non-blocking IO+IO multiplexing
多线程服务器编程模型:1)每个请求创建一个线程,使用阻塞式IO
                                           2)使用线程池,使用阻塞式IO

                                           3)使用non-blocking IO+IO multiplexing

一个多线程服务程序中的线程有:1)IO线程
                                                           2)计算线程

                                                           3)其他使用线程

IO模型           

             同步阻塞IO(read/write) :  用户空间的应用程序会执行一个系统调用(内核的上下文切换),导致应用程序阻塞,直到系统调用完成为止(数据传输完成或者出错)。应用程序处于不消耗cpu只是简单等待响应的状态

            同步非阻塞IO(read/write,打开O_NONBLOCK标志):  意味着IO操作不会立即完成,需要应用程序调用多次来等待(可能会返回错误代码EAGAIN 或 EWOULDBLOCK)。应用程序执行忙碌等待,直到数据可用。会引入IO延时,导致整体数据吞吐量降低。

            异步阻塞IO(select/poll/epoll/kqueue): 带有阻塞通知(IO multiplexing) 的非阻塞 I/O。IO multiplexing是先构造一张有关描述符的列表,然后调用其中一个函数,直到这些描述符中的一个准备好进行IO时,函数才返回。(select/poll均采用轮询的方式检查描述符,select还有描述符的限制。epoll在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。epoll返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可。在linux上建议选择epoll)  常用模式

            异步非阻塞IO:  应用程序的请求会立即返回。在后台处理请求时,应用程序可以执行其他的操作,当响应返回时,就会产生一个信号或执行一个基于线程的回调函数完成IO处理过程。(可以参考linux上AIO,没用过,不是很清楚)

            区别: 同步阻塞IO操作开始时阻塞应用程序,不可能同时重叠进行处理和 I/O 操作
                        同步非阻塞IO允许处理和 I/O 操作重叠进行,但是这需要应用程序根据重现的规则来检查 I/O 操作的状态
                        异步阻塞IO对通知事件进行阻塞,而不是对 I/O 调用进行阻塞
                        异步非阻塞 I/O 允许处理和 I/O 操作重叠进行,包括 I/O 操作完成的通知

线程同步

               多个控制线程共享相同内存时,需要确保每个线程看到一致的数据。(比如你在使用一个对象时,要避免其他线程析构这个对象或者相反,c++多线程编程主要问题就是要解决或避免这些竞争)

               线程安全: 从多个线程访问时,其表现出正确的行为

                                    调用端代码无需额外的同步或其他协调动作(C++ 标准库里的大多数类都不是线程安全的,这些类通常需要在外部加锁)

              同步编程原则:尽量减少内存的共享,减少同步的场合,可以考虑immutable 对象
                                          使用高级的并发编程构件(还没用过)

                                          使用底层同步原语 (primitives) 时,只用非递归的互斥器和条件变量,偶尔用一用读写

              底层同步原语
                                        1)互斥锁
                                        2)读写锁
                                        3)条件变量

互斥锁

             它保护了临界区,一个时刻最多只能有一个线程在临界区内活动。
            1)用 RAII 手法封装 mutex 的创建、销毁、加锁、解锁 (离开作用域后自动解锁)
            2)只用非递归的 mutex(即不可重入的 mutex)(方便查找逻辑错误)
            3)不手工调用 lock() 和 unlock() 函数
一个用于windows和linux的锁类(异常类MutexFailed可自己定义,可直接派生自上篇异常处理的异常基类,也可以删除掉)
class  Mutex
{
public:

    Mutex() throw (MutexFailed);
    ~Mutex();
    void lock() throw (MutexFailed);
    bool trylock() throw (MutexFailed);
    void unlock() throw (MutexFailed);

private:
    void* pmutex_;
};
class ScopedLock//自动加锁,解锁
{
public:
    explicit ScopedLock(Mutex& mutex) ://禁止隐式转化
    mutex_(mutex)
    {
        mutex_.lock();
    }
    ~ScopedLock() { mutex_.unlock(); }

private:
    ScopedLock(const ScopedLock&);    // can't copy
    ScopedLock& operator =(const ScopedLock&);// can't assign

    Mutex& mutex_;    
};

#if defined(__WIN32__) || defined(_WIN32)
        typedef HANDLE mutex_t;
#else
        typedef pthread_mutex_t mutex_t;    
#endif
    
#if defined(__WIN32__) || defined(_WIN32)
    static mutex_t impl_val(void* p)
        { return *static_cast<mutex_t*>(p); }
#else
        static mutex_t* impl_ptr(void* p)
        { return static_cast<mutex_t*>(p); }
#endif


Mutex::Mutex() throw (MutexFailed)
    : pmutex_(new mutex_t)
{
#if defined(__WIN32__) || defined(_WIN32)
    impl_val(pmutex_) = CreateMutex((LPSECURITY_ATTRIBUTES) 0, FALSE,
            (LPCTSTR) 0);
    if (!impl_val(pmutex_))
        throw MutexFailed("CreateMutex failed");
#else
    register int rc;//明确要求CPU把变量始终保存在寄存器里面,直至它消亡.现在大多数编译器自己优化,不一定有用
    if ((rc = pthread_mutex_init(impl_ptr(pmutex_), 0)))
        throw MutexFailed(strerror(rc));
#endif
}

Mutex::~Mutex()
{

#if defined(__WIN32__) || defined(_WIN32)
    CloseHandle(impl_val(pmutex_));
#else
    pthread_mutex_destroy(impl_ptr(pmutex_));
#endif
    delete impl_ptr(pmutex_);
}


void Mutex::lock() throw (MutexFailed)
{
#if defined(__WIN32__) || defined(_WIN32)
    if (WaitForSingleObject(impl_val(pmutex_), INFINITE) == WAIT_OBJECT_0)
        return;
    throw MutexFailed("WaitForSingleObject failed");
#else
    register int rc;
    if ((rc = pthread_mutex_lock(impl_ptr(pmutex_))))
        throw MutexFailed(strerror(rc));
#endif
}


bool Mutex::trylock() throw (MutexFailed)
{
#if defined(__WIN32__) || defined(_WIN32)
    switch (WaitForSingleObject(impl_val(pmutex_), 0)) {
        case WAIT_TIMEOUT:
            return false;
        case WAIT_OBJECT_0:
            return true;
        default:
            throw MutexFailed("WaitForSingleObbject failed");
    }
#else
    register int rc;
    if ((rc = pthread_mutex_trylock(impl_ptr(pmutex_))) == 0)
        return true;
    if (rc == EBUSY)
        return false;
    throw MutexFailed(strerror(rc));
#endif
}


void Mutex::unlock() throw (MutexFailed)
{
#if defined(__WIN32__) || defined(_WIN32)
    if (!ReleaseMutex(impl_val(pmutex_)))
        throw MutexFailed("ReleaseMutex failed");
#else
    register int rc;
    if ((rc = pthread_mutex_unlock(impl_ptr(pmutex_))))
        throw MutexFailed(strerror(rc));
#endif
}

PS:能用单线程完成的尽量用单线程,多线程实现上比较麻烦,需要分析可能出现的 race condition,对线程安全要求较高,尽量不用跨线程对象。还需要更多学习和练习。

         参考blog:http://blog.csdn.net/solstice/article/category/642322

         书籍:《UNIX环境高级编程》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值