sylar--线程模块
本模块是基于pthread实现的。自己封装了pthread,并提供信号量、互斥锁、读写锁和自旋锁。
线程模块定义的类
class Semaphore:封装了信号量
class Mutex:封装了互斥锁
class RWMutex:封装了读写锁
class SpinLock:封装了自旋锁
class Thread:封装了线程pthread
对于上述相关的信号量和锁,如果没有调用对应init函数进行初始化,任何尝试使用相关信号量和锁的的行为,都是未定义的
在调用destroy之前,应该确保没有线程在使用,否则destroy是未定义行为
互斥锁与自旋锁的区别?
互斥锁,是sleep-waiting类型的锁。需要消耗大量的系统资源来建立锁。当线程被阻塞时,线程的调度状态被修改,线程被加入等待队列。当锁可用是,获取锁之前,线程将从等待队列取出并更改其调度状态。在阻塞期间,不消耗CPU资源
互斥锁适用于可能会被阻塞很长时间的场景
自旋锁,是busy-waiting类型的锁。需要消耗很少的系统资源建立锁。spinlock不会使线程进入睡眠状态,但会一直消耗CPU时间
自旋锁适用于仅需要阻塞很短时间的场景
class Semaphore
成员变量
private:sem_t m_semaphore; //信号量,是一个长整型数
成员函数
- Semaphore(uint32_t count = 0):初始化信号量,调用sem_init实现
- ~Semaphore():销毁信号量,调用sem_destroy实现
- void wait():获取信号量,调用sem_wait实现
- void notify():释放信号量,调用sem_post实现
- int sem_init(sem_t *sem, int pshared, unsigned int value)
sem是指向要初始化的信号量的指针
pshared指定信号量是进程内共享(0)还是跨进程共享(~0)
value是信号量的初始值
成功返回0,否则返回-1
- int sem_destroy(sem_t *sem)
确保没有任何线程或进程正在使用信号量,才能调用此函数,否则会导致未定义行为
- int sem_wait(sem_t *sem)
sem是指向要获取的信号量的指针。若调用时信号量的值大于0,则递减该值并立即返回,否则当前线程被阻塞
- int sem_post(sem_t *sem)
用于向指定的命名或未命名信号量发送信号,使其计数器+1。若有线程在等待,则将其中一个唤醒。成功返回0,失败返回-1并设置errno
private:Semaphore(const Semaphore&) = delete; //禁止拷贝构造Semaphore(const Semaphore&&) = delete; //禁止移动构造Semaphore& operator=(const Semaphore&) = delete; //禁止拷贝赋值
上述三个函数,通过private+delete,来明确禁止信号量对象的拷贝和移动操作。
信号量通常包含操作系统资源,拷贝会导致资源管理混乱
移动后原对象和新对象的资源句柄可能同时有效,导致未定义行为
class Mutex
成员变量
private:pthread_mutex_t m_mutex; //互斥锁
成员函数
- Mutex():初始化互斥锁对象,调用pthread_mutex_init实现
- ~Mutex():销毁已初始化的互斥锁对象,调用pthread_mutex_destroy实现
- lock():加锁操作,调用pthread_mutex_lock实现
- unlock():解锁操作,调用pthread_mutex_unlock实现
- int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
mutex:指向要初始化的互斥锁对象的指针
attr:指向互斥锁属性对象的指针,置为nullptr会使用默认属性
成功时返回0,失败时返回错误码并设置errno
- int pthread_mutex_destroy(pthread_mutex_t *mutex)
mutex:指向要销毁的互斥锁对象的指针
成功时返回0,失败时返回错误码并设置errno
- int pthread_mutex_lock(pthread_mutex_t *mutex)
mutex:指向要加锁的互斥锁对象的指针
成功时返回0,失败时返回错误码并设置errno
当前线程调用此函数时,如果该互斥锁没有被其它线程持有,则会获取该互斥锁并标记为被持有;否则,当前线程被阻塞,直到锁被释放,再重新尝试加锁
- int pthread_mutex_unlock(pthread_mutex_t *mutex)
mutex:指向要解锁的互斥锁对象的指针
成功时返回0,失败时返回错误码并设置errno
当一个线程调用此函数时,该互斥锁将被标记为未被持有。如果有其它线程在等待该锁,则将其中一个线程唤醒
class RWMetux
成员变量
private:pthread_rwlock_t m_lock; //读写锁
成员函数
- RWMutex():初始化一个读写锁对象,调用pthread_rwlock_init实现
- ~RWMutex():销毁一个读写锁,调用pthread_rwlock_destroy实现
- rdlock():加读锁,调用pthread_rwlock_rdlock实现
- wrlock():加写锁,调用pthread_rwlock_wrlock实现
- unlock():释放锁,调用pthread_rwlock_unlock实现
读写锁是一种同步机制,用于在多线程环境下对共享资源进行访问控制。允许多个进程同时读取共享资源,但只允许一个线程写入共享资源
- int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
rwlock:指向要初始化的读写锁对象的指针
attr:指向读写锁属性对象的指针,为nullptr时使用默认属性
成功时返回0,失败时返回错误码并设置errno
- int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
rwlock:指向要销毁的读写锁对象的指针
成功时返回0,失败时返回错误码并设置errno
- int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
成功时返回0,失败时返回错误码并设置errno
允许多个线程同时读取共享资源,但不能写入
如果有线程持有写锁,其它线程将被阻塞直到写入锁被释放
- int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
成功时返回0,失败时返回错误码并设置errno
阻止其它线程读取或写入共享资源,直到该线程释放写锁
调用此函数时,若其它线程持有读锁或写锁,此函数会被阻塞,直到所有读、写锁释放
- int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
成功时返回0,失败时返回错误码并设置errno
class SpinLock
成员变量
private:pthread_spinlock_t m_mutex; //自旋锁
成员函数
- Spinlock():初始化自旋锁,调用pthread_spin_init实现
- ~Spinlock():销毁自旋锁,调用pthread_spin_destroy实现
- lock():加锁,调用pthread_spin_lock实现
- unlock():解锁,调用pthread_spin_unlock实现
- int pthread_spin_init(pthread_spinlock_t *lock, int pshared)
lock:指向初始化的自旋锁的指针
pshared:自旋锁是进程内共享(0)还是跨进程共享(~0)
成功时返回0,失败时返回错误码并设置errno
- int pthread_spin_destroy(pthread_spinlock_t *lock)
lock:指向要被销毁的自旋锁的指针
成功则返回0,失败时返回错误码并设置errno
- int pthread_spin_lock(pthread_spinlock_t *lock)
若该锁没有被其它线程持有,则当前线程获取自旋锁,否则阻塞,直到其它线程释放锁
成功时返回0,失败时返回错误码并设置errno
- int pthread_spin_unlock(pthread_spinlock_t *lock)
成功时返回0,失败时返回错误码并设置errno
class Thread
成员变量
private:pid_t m_id = -1; //线程idpthread_t m_thread = 0; //线程结构std::function<void()> m_cb; //线程执行函数std::string m_name; //线程名称Semaphore m_semaphore; //信号量
成员函数
- Thread::Thread(std::function<void()> cb, const std::string &name):初始化线程执行函数、线程名称,通过pthread_create创建新线程
- Thread::~Thread():检查m_thread是否存在,存在则调用pthread_detach来分离已结束的线程。在析构函数中分离线程,可以避免主线程退出时出现悬挂线程,从而防止内存泄漏
- void Thread::join():调用pthread_join来实现等待线程执行完成
- void* Thread::run(void* arg):线程执行函数。通过信号量,在构造函数中创建线程后一直阻塞,直到run方法运行并通知信号量,构造函数才会返回
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
thread:指向pthread_t类型的指针,用于返回新线程的id
attr:指向pthread_attr_t类型的指针,包含新线程属性信息,设置为nullptr使用默认值
start_routine:指向新线程函数的指针,该函数将在新线程中运行
arg:指向新线程函数的参数的指针,不需要该参数时,可设置为nullptr
新线程将在与调用pthread_crreate函数的线程并发执行的情况下运行
- int pthread_detach(pthread_t thread)
将指定线程标记为”可分离状态“,被分离的线程终止时,系统自动回收其资源
线程一旦分离,不能再通过pthread_join等待其结束
成功时返回0,否则返回错误码并设置errno
- int pthread_join(pthread_t thread, void **retval)
thread:到等待的线程id
retval:指向指针的指针,用于存储线程返回的值。若不需要获取返回值,设置为nullptr
阻塞机制,用于等待指定线程的终止并获取其返回值,同时回收该线程所使用的资源
成功时返回0,表示等待线程成功退出
private:Thread(const Thread&) = delete;Thread(const Thread&&) = delete;Thread& operator= (const Thread&) = delete;
1、信号量在上述代码中有什么用?
2、Mutex、Spinlock和CSA的性能如何?
3、为什么要在FileLogAppender::Log中周期性reopen?
4、RALL是什么?
5、不能拷贝和移动类对象时,应该怎么做?
6、如何保证在出构造函数之前,确保线程先跑起来?