一、线程创建
1、 linux api (C语言)
int pthread_create(...);
void pthread_exit(void *retval);
int pthread_join(pthread_t pthread, void **retval);
int pthread_datach(pthread_t thread);
int pthread_cancel(pthread_t pthread);
int pthread_attr_init(pthread_attr_t* attr);
2、C++11标准库
std::thread ;
t.joinable();
t.join();
t.detach();
t.get_id();
this_thread::get_id();
this_thread::sleep_for(dur);
this_thread::sleep_util(tp);
this_thread::yield(); // 放弃线程时间片,系统重新轮询
二、线程并发与死锁
- 线程默认栈大小: (linux)8MB
- 并发线程数设置
- IO密集型,Nthreads = 2Ncpu;
- 计算密集型,Nthreads = Ncpu+1;
- 死锁四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用;
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺;
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;
- 死锁线程查找与定位:
- 1.ps获取id->pstack多次打印堆栈
- 2.gdb --attach -p pid -> info threads
->thread n ->bt - 3.top命令查看cpu占用最高的线程,大概判定死锁线程->再查看堆栈
三、线程同步
概念
互斥锁、信号量、条件变量、原子操作、临界区(windows)
1、线程锁
(1) linux api (C语言)
互斥锁:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态创建
// 动态创建
pthread_mutex_t *mutexptr = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(mutexptr,NULL);
pthread_mutex_lock(mutexptr);
pthread_mutex_unlock(mutexptr);
pthread_mutex_destroy(mutexptr);
读写锁:(只对写操作唯一)
如果有线程读数据,则允许其它线程执行读操作,但不允许写操作;
如果有线程写数据,则其它线程都不允许读、写操作;
pthread_rwlock_t *restrict rwlock;
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
自旋锁: (线程申请自旋锁时处于一段忙等待时间)
自旋锁阻塞后不会让出CPU,会一直忙等待,直到得到锁;
自旋锁在用户态较少用,而在内核态使用的比较多;
spinlock_t lock;
pthread_spin_init(&lock);
pthread_spin_destroy(&lock);
pthread_spin_lock(&lock);
pthread_spin_unlock(&lock);
(2)C++11标准库
std::mutex m;
m.lock() ;
m.unlock() ;
m.try_lock();
m.try_lock_for(dur);
m.try_lock_until(tp);
lock_guard<std::mutex> lg(m);
unique_lock<std::mutex> ul(m); // 与lock_guard接口相同,另外提供owns_lock()、try_lock()、try_lock_for(dur)、try_lock_until(tp)方法
2、条件变量
条件变量是由互斥量保护的,线程在改变条件变量状态前必须先锁住互斥量。
条件变量与互斥量一起使用的时候,允许线程以无竞争的方式等待特定的条件发生。
(1) linux api (C语言)
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
pthread_cond_destroy(&cond);
pthread_cond_wait(&cond, &lock);
pthread_cond_broadcast(&cond);
(2)C++11标准库
std::condition_variable cv;
cv.notify_one(); // 唤醒一个等待者线程
cv.notify_all(); // 唤醒所有等待线程
cv.wait(ul); // 使用unique_lock来等待通知
cv.wait_for(ul, dur); // 使用unique_lock来等待通知,等待期限是dur
cv.wait_until(ul, tp)l; //使用unique_lock来等待通知,直到时间点tp;
3、信号量
信号量对象要放在全局数据区
linux api(C语言)
#include <semaphore.h>
无名信号量:
sem_t *sem;
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t * sem);
int sem_getvalue(sem_t *sem, int *sval);
int sem_wait(sem_t *sem); // 相对于P操作
int sem_trywait(sem_t *sem); // 非阻塞P
int sem_post(sem_t *sem); // 相当于V操作
4、原子操作
适用于基本数据类型、pointer类型
C语言:
atomic_<type>, 示例atomic_bool
C++11:
std::atomic<T> a = val;
atomic_init(&a, val);
a.is_lock_free(); // atomic内部是否使用lock
a.store(val);
a.load();