Linux 多线程互斥与同步

编译选项

编译时要加上-pthread

gcc xxx.c -o xxx -pthread

API

Linux多线程基础函数使用

#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函数是主线程,当主线程退出时一个进程下的全部线程会全部退出

线程清理

pthread_cleanup_push_pthread

/* 清除回调函数 */
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);
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值