编译选项
编译时要加上-pthread
gcc xxx.c -o xxx -pthread
API
#include <pthread.h>
/*
pthread_create()用于创建线程
thread: 接收创建的线程的 ID
attr: 指定线程的属性//一般传NULL
start_routine:指定线程函数
arg: 给线程函数传递的参数
成功返回 0, 失败返回错误码
*/
int pthread_create(pthread_t * thread, const pthread_attr_t *attr,void *(*start_routine) ( void *),void *arg);
/* 获取当前线程ID
在自己线程内调用 */
pthread_t pthread_self(void);
/*
在自己线程内调用,主动退出
pthread_exit()退出线程
retval:指定退出信息
*/
int pthread_exit( void *retval);//retval对应pthread_join的retval
/*
pthread_join()阻塞当前线程,直到thread指定的线程退出时结束阻塞
retval:接收 thread 线程退出时,指定的退出信息
*/
int pthread_join(pthread_t thread, void **retval);
/* 一个线程(调用者线程)请求取消另一个线程的执行
被动退出
*/
int pthread_cancel(pthread_t thread);
/* pthread_detach函数用于将一个线程标记为分离状态,从而使得线程的资源在其终止时可以自动释放,而不需要其他线程调用pthread_join函数来等待其终止。这样可以避免出现僵尸线程(zombie thread),从而简化了对线程资源的管理。*/
int pthread_detach(pthread_t thread);
线程生命周期
main函数是主线程,当主线程退出时一个进程下的全部线程会全部退出
线程清理
/* 清除回调函数 */
void clean(void* arg){
printf("调用清除函数\n");
}
void* handle(void* arg){
pthread_cleanup_push(clean, NULL);
//函数体 函数体一定要放在两个函数中间,不然会报错
pthread_cleanup_pop(1); //1:调用回调函数, 0:不调用
}
互斥与同步
互斥锁、读写锁、自旋锁区别
-
互斥锁:
用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒 -
读写锁:
分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。 -
自旋锁:
在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费CPU资源。
互斥
#include <pthread.h>
#include <time.h>
// 初始化一个互斥锁。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 对互斥锁上锁,调用该函数时,若互斥锁未加锁,则上锁,返回 0;
// 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 对指定的互斥锁解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 销毁指定的一个互斥锁。互斥锁在使用完毕后,
// 必须要对互斥锁进行销毁,以释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
读写锁
- 读锁:同个时间内可以多个线程同时获取读取锁,就是在读锁已经上锁的情况下,其他线程也还是可以继续给上一次读锁,但是上多少次读锁就要解多少次锁,因为读写锁会记录上锁数
- 写锁:同个时间内只能有一个线程持有写入锁,当一个线程获取到定稿锁后,即给写入锁上锁了之后,直到这个线程解锁之后其他线程才能继续获取到读/写锁,在其他线程是不能给这个写入锁解锁的,只能是上锁的线程才能解锁
- 获取到读取锁之后可以获取到写入锁,但是有线程获取到写入锁之后任何线程都不能再获取读/写锁了
- 获取锁:上锁的意思;
/* 初始化读写锁 */
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
/* 上读锁:阻塞 */
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
/* 上读锁:非阻塞 成功返回0 */
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//尝试加读锁,没锁上就立即返回
/* 上写锁:阻塞 */
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
/* 上写锁:非阻塞 成功返回0 */
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//尝试(没锁上就立即返回)加锁
/* 解锁 */
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
/* 销毁锁 */
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void* reader_thread(void* arg) {
pthread_rwlock_t* rwlock = (pthread_rwlock_t*)arg;
for (int i = 0; i < 5; i++) {
if (pthread_rwlock_rdlock(rwlock) == 0) {
printf("Reader thread: reading\n");
pthread_rwlock_unlock(rwlock);
} else {
printf("Reader thread: failed to acquire read lock\n");
}
sleep(1);
}
return NULL;
}
void* writer_thread(void* arg) {
pthread_rwlock_t* rwlock = (pthread_rwlock_t*)arg;
for (int i = 0; i < 5; i++) {
if (pthread_rwlock_wrlock(rwlock) == 0) {
printf("Writer thread: writing\n");
pthread_rwlock_unlock(rwlock);
} else {
printf("Writer thread: failed to acquire write lock\n");
}
sleep(1);
}
return NULL;
}
int main(void) {
pthread_t reader, writer;
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_create(&reader, NULL, reader_thread, (void*)&rwlock);
pthread_create(&writer, NULL, writer_thread, (void*)&rwlock);
pthread_join(reader, NULL);
pthread_join(writer, NULL);
pthread_rwlock_destroy(&rwlock);
return 0;
}
自旋锁
API函数
1. 销毁自旋锁
int pthread_spin_destroy(pthread_spinlock_t *);
2. 初始化自旋锁
pshared:
PTHREAD_PROCESS_SHARED 其他进程的线程也可以访问使用
PTHREAD_PROCESS_PRIVATE 只有当前进程内的线程可以使用
int pthread_spin_init(pthread_spinlock_t *, int pshared);
3. 自旋锁上锁(阻塞)
int pthread_spin_lock(pthread_spinlock_t *);
4. 自旋锁上锁(非阻塞)
int pthread_spin_trylock(pthread_spinlock_t *);
5. 自旋锁解锁
int pthread_spin_unlock(pthread_spinlock_t *);
以上函数成功都返回0.
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
void* a_pthread_handle(void* arg){
int i=0;int err;
for(;;i++){
if(i % 15 == 0){
if(pthread_spin_lock((pthread_spinlock_t*)arg) == 0){
printf("线程A获取锁,不让线程B获取到锁,阻塞线程B4秒\n");
}
sleep(4);
pthread_spin_unlock((pthread_spinlock_t*)arg);
}
sleep(1);
}
}
void* b_pthread_handle(void* arg){
for(;;){
if(pthread_spin_lock((pthread_spinlock_t*)arg) == 0){
printf("线程B获取到锁了\n");
pthread_spin_unlock((pthread_spinlock_t*)arg);
}else{
printf("线程B没有获取到锁\n");
}
sleep(1);
}
}
int main(void){
pthread_t a_pthread, b_pthread;
pthread_spinlock_t spinlock;
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
if(pthread_create(&a_pthread, NULL, a_pthread_handle, (void*)&spinlock) != 0){
perror("pthread_create\n");
}
if(pthread_create(&b_pthread, NULL, b_pthread_handle, (void*)&spinlock) != 0){
perror("pthread_create\n");
}
pthread_join(a_pthread, NULL);
}
同步——信号量
#include <semaphore.h>
/*pshared:
PTHREAD_PROCESS_SHARED = 1 其他进程的线程也可以访问使用
PTHREAD_PROCESS_PRIVATE = 0 只有当前进程内的线程可以使用
value 信号量初始值*/
int sem_init(sem_t *sem, int pshared, unsigned int value);
/* 获取一个信号量,信号量的值-1,当信号量为0时无法获取成功 */
int sem_wait(sem_t *sem); //阻塞等待
int sem_trywait(sem_t *sem); //百阻塞等待
/* 释放一个信号量 */
int sem_post(sem_t *sem);
/* 获取信号量 sem 的当前值,把该值保存在 sval,若有 1 个或者多个线程正在调用 sem_wait 阻塞在该信号量上,该函数返回阻塞在该信号量上进程或线程个数 */
int sem_getvalue(sem_t *sem, int *sval);
/* 销毁信号量 */
int sem_destroy(sem_t *sem);
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <semaphore.h>
void* a_pthread_handle(void* arg){
int i=0;int err;
for(;;i++){
if(i % 10 == 0){ //10秒释放一个信号量
printf("释放一个信号量\n");
sem_post((sem_t*)arg);
}
sleep(1);
}
}
void* b_pthread_handle(void* arg){
for(;;){
sem_wait((sem_t*)arg);
printf("线程B获取到一次信号量\n");
sleep(1);
}
}
int main(void){
pthread_t a_pthread, b_pthread;
sem_t sem;
sem_init(&sem, PTHREAD_PROCESS_PRIVATE, 1);
if(pthread_create(&a_pthread, NULL, a_pthread_handle, (void*)&sem) != 0){
perror("pthread_create\n");
}
if(pthread_create(&b_pthread, NULL, b_pthread_handle, (void*)&sem) != 0){
perror("pthread_create\n");
}
pthread_join(a_pthread, NULL);
}