《信息安全系统设计与实现》第九周学习笔记

发布时间 2023-11-01 21:54:05作者: 20211309宁心宇

一、 第五章 定时器及时钟服务

1、并行计算

是一种计算方案,它尝试使用多个执行并行算法的处理器更快速的解决问题

顺序算法与并行算法

并行性与并发性

并行算法只识别可并行执行的任务。CPU系统中,并发性是通过多任务处理来实现的

2、线程

线程的原理:某进程同一地址空间上的独立执行单元

线程的优点

线程创建和切换速度更快
线程的响应速度更快
线程更适合并行运算

线程的缺点

线程需要来自用户的明确同步
库函数不安全
单CPU系统中,线程解决问题实际上要比使用顺序程序慢

3、线程操作

线程可在内核模式或用户模式下执行
其中涉及Linux下的pthread并发编程

线程管理函数

创建线程

pthread_create()
int pthread_create(pthread_t *pthread_id,pthread_attr_t *attr,void *(*func)(void*),void *arg)

其中:
1、pthread_id是指向pthread_t类型变量的指针
2、attr如果是NULL,将使用默认属性创建线程

线程ID

int pthread_equal(pthread_t t1,pthread_t t2);

不同的线程,返回0,否则返回非0

线程终止

int pthread_exit(void *status);

线程连接

int pthread_join(pthread_t thread,void **status_ptr)

线程同步

竞态条件:修改结果取决于线程执行顺序

互斥量

在pthread中,锁被称为互斥量

pthread_mutex_lock(&m);
access shared data object;
pthread_mutex_unlock(&m);

死锁预防

死锁是一个状态,在这种状态下,许多执行实体相互等待,无法继续进行下去

条件变量

条件变量可以通过两种方法进行初始化:静态方法,动态方法

生产者-消费者问题

共享全局变量

int buf[NBUF];
int head,tail;
int data;

4.信号量

信号量是进程同步的一般机制
信号量和条件变量

屏障

线程连接操作允许某线程等待其他线程终止
在pthread中可以采用的机制是屏障以及一系列屏障函数

Linux中的线程

进程和线程都是由clone()系统调用创建的具有以下原型

int clone(int (*fn)(void*),void *child_stack,int flags,void *arg)

信号量与条件变量的区别:

条件变量、互斥锁——主要用于线程间通信
pthread_cond_wait()
pthread_cond_wait(&m_cond,&m_mutex); 指的是等待条件变量,总和一个互斥锁结合使用。

pthread_cond_wait() 函数执行时先自动释放指定的互斥锁,然后等待条件变量的变化;在函数调用返回之前(即wait成功获得cond条件的时候),会自动将指定的互斥量重新锁住(即在“等待的条件变量满足条件时,会重新锁住指定的锁”)。

二、代码实践

1、利用线程计算矩阵的和

代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define ROWS 3
#define COLS 3
#define THREADS_NUM 3

int matrix[ROWS][COLS] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int sum[THREADS_NUM] = {0};
pthread_t threads[THREADS_NUM];

void *thread_func(void *arg) {
    int tid = *(int *)arg;
    int start = tid * ROWS / THREADS_NUM;
    int end = (tid + 1) * ROWS / THREADS_NUM;
    for (int i = start; i < end; i++) {
        for (int j = 0; j < COLS; j++) {
            sum[tid] += matrix[i][j];
        }
    }
    pthread_exit(NULL);
}

int main() {
    int thread_args[THREADS_NUM];
    for (int i = 0; i < THREADS_NUM; i++) {
        thread_args[i] = i;
        pthread_create(&threads[i], NULL, thread_func, &thread_args[i]);
    }
    for (int i = 0; i < THREADS_NUM; i++) {
        pthread_join(threads[i], NULL);
    }
    int total_sum = 0;
    for (int i = 0; i < THREADS_NUM; i++) {
        total_sum += sum[i];
    }
    printf("The sum of the matrix is %d\n", total_sum);
    return 0;
}

代码分析:

该代码中,我们首先定义了一个 3x3 的矩阵和 3 个线程。每个线程计算矩阵的一部分,并将结果存储在 sum 数组中。最后,我们将所有线程计算出的结果相加,得到矩阵的总和。
主函数中:为每个线程创建一个参数数组 thread_args,然后创建 THREADS_NUM 个线程,并将线程 ID 和参数数组传递给线程函数 thread_func。
线程函数中:根据线程 ID 计算出该线程要处理的矩阵部分,然后计算该部分的和并存储在 sum 数组中。
主函数等待所有线程执行完毕后,将 sum 数组中的所有元素相加,得到矩阵的总和,并输出结果。

遇到的问题:

缺少相关的库

2、生产者·消费者问题

代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUFFER_SIZE 5
#define NUM_ITEMS 10

int buffer[BUFFER_SIZE];
int count = 0;
int in = 0;
int out = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t full = PTHREAD_COND_INITIALIZER;
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;

void *producer(void *arg) {
    for (int i = 0; i < NUM_ITEMS; i++) {
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&full, &mutex);
        }
        buffer[in] = i;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        printf("Produced item: %d\n", i);
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

void *consumer(void *arg) {
    for (int i = 0; i < NUM_ITEMS; i++) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&empty, &mutex);
        }
        int item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        printf("Consumed item: %d\n", item);
        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t producer_thread, consumer_thread;
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    return 0;
}

代码分析

使用一个大小为 BUFFER_SIZE 的缓冲区来模拟生产者和消费者之间的共享数据。生产者线程负责向缓冲区中生产数据,消费者线程负责从缓冲区中消费数据。
生产者线程在每次生产完一个数据项后,会先获取互斥锁 mutex,然后检查缓冲区是否已满(count == BUFFER_SIZE)。如果缓冲区已满,则调用 pthread_cond_wait 函数等待消费者线程发出的非空信号(empty)。一旦收到非空信号,生产者线程将数据写入缓冲区,并更新相关变量。最后,生产者线程发出缓冲区已满的信号(full),释放互斥锁 mutex。
消费者线程的逻辑类似,它在每次消费一个数据项后,会先获取互斥锁 mutex,然后检查缓冲区是否为空(count == 0)。如果缓冲区为空,则调用 pthread_cond_wait 函数等待生产者线程发出的非满信号(full)。一旦收到非满信号,消费者线程从缓冲区中读取数据,并更新相关变量。最后,消费者线程发出缓冲区已空的信号(empty),释放互斥锁 mutex。
在主函数中,我们创建一个生产者线程和一个消费者线程,并使用 pthread_join 函数等待它们的结束。这样,生产者和消费者就可以并发地执行。

显然,输出结果符合预期。生产者线程生产了 10 个数据项,消费者线程消费了这些数据项,并且生产者和消费者交替进行,以避免缓冲区溢出或下溢。
需要注意的是,由于线程的并发执行,生产者和消费者的输出顺序可能会不同,但每个数据项的生产和消费都是正确的。
由于缓冲区大小为 5,因此在生产者和消费者之间存在一定的同步和竞争关系,这也体现了线程间通信和同步的重要性。

三、苏格拉底问答