pthread_t
,线程对象- 创建线程
// 成功返回0 int pthread_create(pthread_t *restrict thread, // 需要创建的线程对象 const pthread_attr_t *restrict attr, // 线程的相关属性,通常设置为null void *(*start_routine)(void *), // 线程执行的函数 void *restrict arg); // 线程执行函数的参数
- 终止线程
void pthread_exit(void *retval); // 用于存储返回值,通常设置为null
- 挂起线程
// 成功返回0 int pthread_join(pthread_t thread, // 需要挂起的线程 void **retval); // 用于保存退出时线程的状态,通常设置为null
pthread_mutex_t
,互斥信号量- 初始化、销毁互斥信号量
// 成功返回0 int pthread_mutex_init(pthread_mutex_t *restrict mutex, // 互斥信号量 const pthread_mutexattr_t *restrict attr); // 互斥信号量的相关属性,通常设置为null int pthread_mutex_destroy(pthread_mutex_t *mutex); // 销毁互斥信号量
- 锁、解锁互斥信号量
// 成功返回0 int pthread_mutex_lock(pthread_mutex_t *mutex); // 锁住互斥信号量,若信号量已经被锁住,则等待 int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试锁住互斥信号量,若信号量已经被锁住,则直接返回 int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁互斥信号量
pthread_cond_t
,条件信号量- 初始化、销毁条件信号量
// 成功返回0 int pthread_cond_init(pthread_cond_t *restrict cond, // 条件信号量 const pthread_condattr_t *restrict attr); // 条件信号量的相关属性,通常设置为null int pthread_cond_destroy(pthread_cond_t *cond); // 销毁条件信号量
- 等待条件信号量
// 成功返回0 // block条件信号量,直到收到条件信号量,再atomically释放互斥信号量 int pthread_cond_wait(pthread_cond_t *restrict cond, // 条件信号量 pthread_mutex_t *restrict mutex); // 互斥信号量 // 功能同pthread_cond_wait(),但block被设定了时间,到时后,直接返回 int pthread_cond_timedwait(pthread_cond_t *restrict cond, // 条件信号量 pthread_mutex_t *restrict mutex, // 互斥信号量 const struct timespec *restrict abstime); // (绝对)等待时间
- 通知条件信号量
int pthread_cond_signal(pthread_cond_t *cond); // unblock至少1个被指定条件信号量block的线程 int pthread_cond_broadcast(pthread_cond_t *cond); // unblock所有被指定条件信号量block的线程
例子,生产者-消费者问题
#include <iostream>
#include <pthread.h>
#include <queue>
#define TIME_OUT 1
struct ThreadParam {
uint32_t thread_id;
uint32_t num_cakes;
};
struct Buffer {
inline void produce_cake(uint32_t id) {
std::cout << id << "th producer produces " << cake_id << "th cake"
<< std::endl;
que.push(cake_id);
cake_id ++;
}
inline uint32_t consume_cake(uint32_t id) {
uint32_t cake = que.front();
que.pop();
std::cout << id << "th consumer consumes " << cake << "th cake"
<< std::endl;
return cake;
}
inline bool empty() { return que.empty(); }
inline bool full() { return que.size() >= MAX_BUFFER_SIZE; }
uint32_t cake_id = 0;
std::queue<uint32_t> que;
const uint32_t MAX_BUFFER_SIZE = 10;
};
Buffer buf;
uint32_t tot_num_cakes = 0;
uint32_t max_num_cakes = 0;
pthread_mutex_t buf_lock;
pthread_cond_t to_produce, to_consume;
void* produce(void* param) {
ThreadParam* tp = (ThreadParam*)param;
for (uint32_t i = 0; i < tp->num_cakes; ++ i) {
pthread_mutex_lock(&buf_lock);
// if buffer is full already, wait the singal to produce
while (buf.full()) {
pthread_cond_wait(&to_produce, &buf_lock);
}
buf.produce_cake(tp->thread_id);
// Tell consumers to consume cakes
pthread_mutex_unlock(&buf_lock);
pthread_cond_broadcast(&to_consume);
}
std::cout << tp->thread_id << "th producer finishes" << std::endl;
pthread_exit(nullptr);
return nullptr;
}
void* consume(void* param) {
ThreadParam* tp = (ThreadParam*)param;
while (true) {
if (tot_num_cakes >= max_num_cakes) {
break;
}
pthread_mutex_lock(&buf_lock);
struct timespec end_time;
end_time.tv_sec = time(nullptr) + TIME_OUT;
end_time.tv_nsec = 0;
// if buffer is empty, wait the signal to consume in the given timelimits
if (buf.empty()) {
pthread_cond_timedwait(&to_consume, &buf_lock, &end_time);
} else {
uint32_t cake = buf.consume_cake(tp->thread_id);
tp->num_cakes ++;
tot_num_cakes ++;
}
pthread_mutex_unlock(&buf_lock);
// Tell producers to produce cakes
pthread_cond_broadcast(&to_produce);
}
std::cout << tp->thread_id << "th consumer finishes" << std::endl;
pthread_exit(nullptr);
return nullptr;
}
int main() {
uint32_t num_cakes = 10;
uint32_t num_threads = 4;
pthread_t threads[num_threads];
ThreadParam tp[num_threads];
max_num_cakes = num_cakes * (num_threads / 2);
pthread_mutex_init(&buf_lock, nullptr);
pthread_cond_init(&to_produce, nullptr);
pthread_cond_init(&to_consume, nullptr);
// Create producers
for (uint32_t i = 0; i < num_threads / 2; ++ i) {
tp[i].thread_id = i;
tp[i].num_cakes = num_cakes;
int rc = pthread_create(&threads[i], nullptr, produce, (void*)&tp[i]);
if (rc) {
std::cerr << "Cannot create new producers, return code [" << rc << "]"
<< std::endl;
exit(-1);
}
}
// Create consumers
for (uint32_t i = num_threads / 2; i < num_threads; ++ i) {
tp[i].thread_id = i;
tp[i].num_cakes = 0;
int rc = pthread_create(&threads[i], nullptr, consume, (void*)&tp[i]);
if (rc) {
std::cerr << "Cannot create new consumers, return code [" << rc << "]"
<< std::endl;
exit(-1);
}
}
for (uint32_t i = 0; i < num_threads; ++ i) {
pthread_join(threads[i], nullptr);
}
for (uint32_t i = num_threads / 2; i < num_threads; ++ i) {
std::cout << i << "th consumer consumes " << tp[i].num_cakes << std::endl;
}
pthread_mutex_destroy(&buf_lock);
pthread_cond_destroy(&to_produce);
pthread_cond_destroy(&to_consume);
return 0;
}