Linux系统编程之多线程

目录

1、进程与线程

2、线程的优势与使用理由

3、多线程的使用

3.1 线程的使用

1.线程创建函数

 2.线程退出函数

3.线程的等待

4.线程脱离

5. 线程ID获取及比较

 6.示例

 3.2 互斥锁的应用

1.互斥锁相关API

2.示例

3.3 条件变量的使用

1. 创建及销毁条件变量

2. 等待

3. 触发

4.示例


1、进程与线程

Linux 线程是在 Linux 操作系统中运行的基本执行单元。线程是进程的一部分,共享同一地址空间和其他资源,但拥有独立的执行流。在 Linux 中,线程被称为轻量级进程(LWP,Lightweight Process),但轻量级进程更多指的是内核线程(kernel thread),而把用户线程(user thread)称为线程。

"进程——资源分配的最小单位,线程——程序执行的最小单位"

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。进程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 

2、线程的优势与使用理由

从上面我们大概了解了进程与线程的区别,使用多线程的理由主要包括资源节俭、方便的通信机制、提高应用程序响应性、有效利用多CPU系统以及改善程序结构。这些优点使得多线程成为处理并发任务的一种强大工具,特别是在需要高效利用系统资源和提高程序性能的场景下。当然,使用多线程也需要注意线程安全性和避免潜在的并发问题。

这部分摘抄于网络:

使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

  使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

  除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

  • 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
  • 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
  • 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

3、多线程的使用

多线程开发在Linux开发平台上已经有成熟的pthread库支持,详细请见下表:

3.1 线程的使用

1.线程创建函数

#include <pthread.h>  注意都是调用这个库

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                                 void *(*start_routine)(void *), void *arg);

参数说明:

  • thread:指向新创建的线程标识符的指针,该标识符用于标识新线程。
  • attr:指向线程属性的指针,用于指定新线程的属性,通常可以设置为 NULL,表示使用默认属性。
  • start_routine:是一个函数指针,指向新线程将要执行的函数。该函数应该接受一个 void* 类型的参数并返回一个 void* 类型的指针。
  • arg:是传递给 start_routine 函数的参数。如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

pthread_create 的返回值为 0 表示成功创建线程,非零表示创建线程失败,返回的错误码可以用 errno 查看。

 2.线程退出函数

单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:

  1)线程只是从启动例程中返回,返回值是线程的退出码。

  2)线程可以被同一进程中的其他线程取消。

  3)线程调用pthread_exit:

函数原型

void pthread_exit(void *retval);
retval是一个无类型指针,线程可以选择在执行过程中退出,并提供一个退出状态。这个状态可以在其他线程中通过调用 pthread_join 来获取,用于了解线程的退出状态。

使用这个函数可以确保线程资源的正确清理。

退出的状态retval变量必须是static类型的,不然系统会随便给你一个变量

3.线程的等待

函数原型

int pthread_join(pthread_t thread, void **retval);

  • pthread_t thread 是要等待的线程的标识符。
  • void **retval 是一个指针,用于存储线程的退出状态。这个参数可以为 NULL,表示不关心线程的退出状态。

pthread_join 函数的返回值为 0 表示成功等待线程结束,非零表示等待失败。如果成功,线程的退出状态将存储在 retval 指向的地址中

4.线程脱离

一个线程或者是可汇合(joinable,默认值),或者是脱离的(detached)。当一个可汇合的线程终止时,它的线程ID和退出状态将留存到另一个线程对它调用pthread_join。脱离的线程却像守护进程,当它们终止时,所有相关的资源都被释放,我们不能等待它们终止,使用pthread_detach函数可以把一个线程标记为脱离状态。如果一个线程需要知道另一线程什么时候终止,那就最好保持第二个线程的可汇合状态。

函数原型

int pthread_detach(pthread_t thread);

  • pthread_t thread 是要设置为可分离状态的线程的标识符。
  • pthread_detach 函数的返回值为 0 表示成功,非零表示失败。
5. 线程ID获取及比较

pthread_t pthread_self(void);

// 返回:调用线程的ID

对于线程ID比较,为了可移植操作,我们不能简单地把线程ID当作整数来处理,因为不同系统对线程ID的定义可能不一样。我们应该要用下边的函数:

 int pthread_equal(pthread_t tid1, pthread_t tid2);

//用于比较两个线程的标识符是否相等

// 返回:若相等则返回非0值,否则返回0

 6.示例
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define THREAD_EXIT_STATUS "happy"

void *start_rtn(void *arg) {
    int data = *((int *)arg);
    printf("data = %d\n", data);
    printf("pthread_pid = %ld\n", pthread_self());

    // 注意:如果线程函数返回的字符串是动态分配的内存,需要在主线程中释放
    //strdup:用于创建一个字符串的副本并返回指向这个副本的指针。
    //方式一:
    //char *exit_status = strdup(THREAD_EXIT_STATUS);
    //方式二:
    static char *exit_status = "happy";`
    pthread_exit((void *)exit_status);
}

int main() {
    pthread_t pthread_pid;
    int dis = 100;
    char *exit_status = NULL;

    // 添加错误处理
    if (pthread_create(&pthread_pid, NULL, start_rtn, (void *)&dis) != 0) {
        perror("Thread creation failed");
        return 1;
    }

    // 添加错误处理
    if (pthread_join(pthread_pid, (void **)&exit_status) != 0) {
        perror("Thread join failed");
        return 1;
    }

    printf("main - - data  = %s\n", exit_status);

    // 释放动态分配的内存
    free(exit_status);

    printf("main pthread_pid = %ld\n", pthread_self());

    return 0;
}

 3.2 互斥锁的应用

互斥锁(Mutex,全名为 Mutual Exclusion)是一种用于多线程编程的同步机制,用于保护共享资源,防止多个线程同时访问或修改这些资源。互斥锁的基本思想是一次只允许一个线程进入临界区(临界区是指访问共享资源的代码段),其他线程需要等待当前线程释放锁后才能进入。互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy。

1.互斥锁相关API
//创建互斥锁对象,PTHREAD_MUTEX_INITIALIZER是一个宏,用于静态初始化互斥锁。在声明互斥锁变量时,可以使用这个宏进行初始化,而无需调用 pthread_mutex_init
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//初始化互斥锁,第二个参数是 NULL 的话,互斥锁的属性会设置为默认属性
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//阻塞调用,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。
int pthread_mutex_lock(pthread_mutex_t *mutex);
//非阻塞调用,当请求的锁正砸被其他线程占用时, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//释放锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//在指定时间内等待互斥锁,超时返回错误
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
2.示例
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

int dis = 0;
pthread_mutex_t mutex;

// 线程1的执行函数
void *func1(void *data) {
    static int ret = 10;
    while (1) {
        pthread_mutex_lock(&mutex);
        printf("func1: %ld\n", pthread_self());
        printf("func1: %s\n", (char *)data);
        printf("func1 -> dis = %d\n", dis++);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    pthread_exit((void *)&ret);
}

// 线程2的执行函数
void *func2(void *data) {
    static int ret = 20;
    while (1) {
        pthread_mutex_lock(&mutex);
        printf("func2: %ld\n", pthread_self());
        printf("func2: %s\n", (char *)data);
        printf("func2 -> dis = %d\n", dis++);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    pthread_exit((void *)&ret);
}

// 线程3的执行函数
void *func3(void *data) {
    static int ret = 30;
    while (1) {
        pthread_mutex_lock(&mutex);
        printf("func3: %ld\n", pthread_self());
        printf("func3: %s\n", (char *)data);
        printf("func3 -> dis = %d\n", dis++);
        pthread_mutex_unlock(&mutex);
        if (dis == 5) {
        //当dis加到5时线程3会退出,其他线程继续循环
            pthread_exit((void *)&ret);
        }
    }
}

int main() {
    pthread_t tidp1, tidp2, tidp3;
    char *arg = "happy bay!!!";
    int *ret;

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建三个线程
    pthread_create(&tidp1, NULL, func1, (void *)arg);
    pthread_create(&tidp2, NULL, func2, (void *)arg);
    pthread_create(&tidp3, NULL, func3, (void *)arg);

    while (1) {
        printf("main -> dis = %d\n", dis++);
        sleep(1);
    }

    // 等待线程1结束
    pthread_join(tidp1, (void **)&ret);
    printf("Thread 1 returned: %d\n", *ret);

    // 等待线程2结束
    pthread_join(tidp2, (void **)&ret);
    printf("Thread 2 returned: %d\n", *ret);

    // 等待线程3结束
    pthread_join(tidp3, (void **)&ret);
    printf("Thread 3 returned: %d\n", *ret);

    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}

3.3 条件变量的使用

条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。

1. 创建及销毁条件变量

#include <pthread.h>
//初始化条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
//销毁条件变量
int pthread_cond_destroy(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号

除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL

2. 等待
#include <pthread.h>
//等待条件变量满足,同时释放互斥锁,使得其他线程可以获取互斥锁
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
// 返回:若成功返回0,否则返回错误编号

pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。

pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。

3. 触发

#include <pthread.h>
//唤醒等待条件变量的一个线程
int pthread_cond_signal(pthread_cond_t cond);
//唤醒等待条件变量的所有线程
int pthread_cond_broadcast(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号

这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。

  注意一定要在改变条件状态以后再给线程发信号。

4.示例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//初值为一个包含所有字段为 0 的结构体
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0;

void *producer(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);

        // 生产者线程更新共享资源
        shared_data++;
        printf("Producer produced item: %d\n", shared_data);
        pthread_mutex_unlock(&mutex);
        // 在解锁之后,通知等待条件的线程
        pthread_cond_signal(&cond);
        // 模拟生产过程
        sleep(1);
    }
}

void *consumer(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);

        // 检查条件,如果条件不满足,则等待
        while (shared_data == 0) {
            pthread_cond_wait(&cond, &mutex);
        }

        // 消费者线程消耗共享资源
        printf("Consumer consumed item: %d\n", shared_data);
        shared_data--;

        pthread_mutex_unlock(&mutex);

        // 模拟消费过程
        sleep(1);
    }
}

int main() {
    pthread_t producer_tid, consumer_tid;

    // 创建生产者线程和消费者线程
    pthread_create(&producer_tid, NULL, producer, NULL);
    pthread_create(&consumer_tid, NULL, consumer, NULL);

    // 等待线程结束
    pthread_join(producer_tid, NULL);
    pthread_join(consumer_tid, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
同步概念 所谓同步,即同时起步,协调一致。不同的对象,对“同步”的理解方式略有不同。如,设备同步,是指在两个设备之间规定一个共同的时间参考;数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持一致;文件同步,是指让两个或多个文件夹里的文件保持一致。等等 而,编程中、通信中所说的同步与生活中大家印象中的同步概念略有差异。“同”字应是指协同、协助、互相配合。主旨在协同步调,按预定的先后次序运行。 线程同步 同步即协同步调,按预定的先后次序运行。 线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。 举例1: 银行存款 5000。柜台,折:取3000;提款机,卡:取 3000。剩余:2000 举例2: 内存中100字节,线程T1欲填入全1, 线程T2欲填入全0。但如果T1执行了50个字节失去cpu,T2执行,会将T1写过的内容覆盖。当T1再次获得cpu继续 从失去cpu的位置向后写入1,当执行结束,内存中的100字节,既不是全1,也不是全0。 产生的现象叫做“与时间有关的错误”(time related)。为了避免这种数据混乱,线程需要同步。 “同步”的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信号间等等都需要同步机制。 因此,所有“多个控制流,共同操作一个共享资源”的情况,都需要同步。 数据混乱原因: 1. 资源共享(独享资源则不会) 2. 调度随机(意味着数据访问会出现竞争) 3. 线程间缺乏必要的同步机制。 以上3点中,前两点不能改变,欲提高效率,传递数据,资源必须共享。只要共享资源,就一定会出现竞争。只要存在竞争关系,数据就很容易出现混乱。 所以只能从第三点着手解决。使多个线程在访问共享资源的时候,出现互斥。 互斥量mutex Linux中提供一把互斥锁mutex(也称之为互斥量)。 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。 资源还是共享的,线程间也还是竞争的, 但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。 但,应注意:同一时刻,只能有一个线程持有该锁。 当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C线程不去加锁,而直接访问该全局变量,依然能够访问,但会出现数据混乱。 所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成数据混乱。 主要应用函数: pthread_mutex_init函数 pthread_mutex_destroy函数 pthread_mutex_lock函数 pthread_mutex_trylock函数 pthread_mutex_unlock函数 以上5个函数的返回值都是:成功返回0, 失败返回错误号。 pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。 pthread_mutex_t mutex; 变量mutex只有两种取值1、0。 pthread_mutex_init函数 初始化一个互斥锁(互斥量) ---> 初值可看作1 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 参1:传出参数,调用时应传 &mutex restrict关键字:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改 参2:互斥量属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。 参APUE.12.4同步属性 1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER; 2. 动态初始化:局部变量应采用动态初始化。e.g. pthread_mutex_init(&mutex, NULL) pthread_mutex_destroy函数 销毁一个互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); pthread_mutex_lock函数 加锁。可理解为将mutex--(或-1) int pthread_mutex_lock(pthread_mutex_t *mutex); pthread_mutex_unlock函数 解锁。可理解为将mutex ++(或+1) int pthread_mutex_unlock(pthread_mutex_t *mutex); pthread_mutex_trylock函数 尝试加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); 加锁与解锁 lock与unlock: lock尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。 unlock主动解锁函数,同时将阻塞在该锁上的所有线程全部唤醒,至于哪个线程先被唤醒,取决于优先级、调度。默认:先阻塞、先唤醒。 例如:T1 T2 T3 T4 使用一把mutex锁。T1加锁成功,其他线程均阻塞,直至T1解锁。T1解锁后,T2 T3 T4均被唤醒,并自动再次尝试加锁。 可假想mutex锁 init成功初值为1。 lock 功能是将mutex--。 unlock将mutex++ lock与trylock: lock加锁失败会阻塞,等待锁释放。 trylock加锁失败直接返回错误号(如:EBUSY),不阻塞。 加锁步骤测试: 看如下程序:该程序是非常典型的,由于共享、竞争而没有加任何同步机制,导致产生于时间有关的错误,造成数据混乱: #include #include #include void *tfn(void *arg) { srand(time(NULL)); while (1) { printf("hello "); sleep(rand() % 3); /*模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误*/ printf("world\n"); sleep(rand() % 3); } return NULL; } int main(void) { pthread_t tid; srand(time(NULL)); pthread_create(&tid, NULL, tfn, NULL); while (1) { printf("HELLO "); sleep(rand() % 3); printf("WORLD\n"); sleep(rand() % 3); } pthread_join(tid, NULL); return 0; } 【mutex.c】 【练习】:修改该程序,使用mutex互斥锁进行同步。 1. 定义全局互斥量,初始化init(&m, NULL)互斥量,添加对应的destry 2. 两个线程while中,两次printf前后,分别加lock和unlock 3. 将unlock挪至第二个sleep后,发现交替现象很难出现。 线程在操作完共享资源后本应该立即解锁,但修改后,线程抱着锁睡眠。睡醒解锁后又立即加锁,这两个库函数本身不会阻塞。 所以在这两行代码之间失去cpu的概率很小。因此,另外一个线程很难得到加锁的机会。 4. main 中加flag = 5 将flg在while中-- 这时,主线程输出5次后试图销毁锁,但子线程未将锁释放,无法完成。 5. main 中加pthread_cancel()将子线程取消。 【pthrd_mutex.c】 结论: 在访问共享资源前加锁,访问结束后立即解锁。锁的“粒度”应越小越好。 死锁 1. 线程试图对同一个互斥量A加锁两次。 2. 线程1拥有A锁,请求获得B锁;线程2拥有B锁,请求获得A锁 【作业】:编写程序,实现上述两种死锁现象。 读写锁 与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。 读写锁状态: 一把读写锁具备三种状态: 1. 读模式下加锁状态 (读锁) 2. 写模式下加锁状态 (写锁) 3. 不加锁状态 读写锁特性: 1. 读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。 2. 读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。 3. 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。 读写锁非常适合于对数据结构读的次数远大于写的情况。 主要应用函数: pthread_rwlock_init函数 pthread_rwlock_destroy函数 pthread_rwlock_rdlock函数 pthread_rwlock_wrlock函数 pthread_rwlock_tryrdlock函数 pthread_rwlock_trywrlock函数 pthread_rwlock_unlock函数 以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。 pthread_rwlock_t类型 用于定义一个读写锁变量。 pthread_rwlock_t rwlock; pthread_rwlock_init函数 初始化一把读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 参2:attr表读写锁属性,通常使用默认属性,传NULL即可。 pthread_rwlock_destroy函数 销毁一把读写锁 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); pthread_rwlock_rdlock函数 以读方式请求读写锁。(常简称为:请求读锁) int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); pthread_rwlock_wrlock函数 以写方式请求读写锁。(常简称为:请求写锁) int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); pthread_rwlock_unlock函数 解锁 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); pthread_rwlock_tryrdlock函数 非阻塞以读方式请求读写锁(非阻塞请求读锁) int pthread_

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值