0
点赞
收藏
分享

微信扫一扫

Flink从入门到实践(一):Flink入门、Flink部署

穆熙沐 2024-02-10 阅读 26

Linux线程 分离和同步与互斥 条件变量

1. 分离线程

  1. 什么是线程分离?
    线程分离是指线程在结束时,操作系统会自动回收其资源,而无需其他线程显式地等待它的结束或调用pthread_join函数。这种机制允许主线程不必关心子线程的状态,从而提高程序的并发性和可维护性。

  2. pthread_detach函数
    pthread_detach 函数是一个用于将线程设置为分离状态的POSIX线程库函数。通过调用这个函数,线程的资源将在其终止时自动回收,无需其他线程调用 pthread_join 来等待该线程的结束。

#include <pthread.h>
int pthread_detach(pthread_t thread);

代码示例:

#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 3

// 线程执行的任务
void *thread_function(void *arg) {
    int thread_id = *((int *)arg);
    int n = 5;

    // 模拟线程执行任务,循环5次
    while (n--) {
        printf("线程 %d 正在运行\n", thread_id);
        sleep(1);  // 每秒打印一次,模拟线程执行任务
    }

    // 线程结束
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];

    // 创建并启动3个线程
    for (int i = 0; i < NUM_THREADS; ++i) {
        thread_ids[i] = i + 1;

        // 创建线程
        if (pthread_create(&threads[i], NULL, thread_function, (void *)&thread_ids[i]) != 0) {
            perror("创建线程失败");
            return -1;
        }

        // 设置线程为分离状态
        if (pthread_detach(threads[i]) != 0) {
            perror("设置线程为分离状态失败");
            return -1;
        }
    }

    // 主线程执行的任务
    while (1) {
        printf("主线程正在运行\n");
        sleep(1);  // 主线程每秒打印一次,模拟执行其他任务
    }

    return 0;
}

运行时,使用 while :; do ps -aL ;sleep 1;done 实时查看。
在这里插入图片描述

这个程序创建了3个线程,每个线程模拟执行任务5次。主线程在一个无限循环中每秒打印一次消息,模拟执行其他任务。这里使用了 pthread_create 来创建线程,pthread_detach 将线程设置为分离状态,确保线程在结束时能够自动释放资源。

2. 线程互斥与互斥量

多线程编程是一种广泛应用于提高程序性能的技术。然而,当多个线程同时访问和修改共享资源时,可能会导致数据竞争(Data Race)和其他并发问题。为了解决这些问题,互斥量(Mutex)被引入,成为多线程编程中的重要工具。

  1. 为什么需要互斥量?
    在多线程环境下,多个线程可能同时访问和修改共享资源,导致竞态条件。竞态条件是一种并发问题,可能引起不确定的行为。为了解决这些问题,我们引入互斥量来保护共享资源,确保一次只有一个线程能够访问它

  2. 临界资源与临界区
    临界资源: 多个线程执行流共享的资源称为临界资源,它可能是一块内存、文件、网络连接等。对这些资源的并发访问可能导致不确定的结果或数据损坏。
    临界区: 是每个线程内部访问临界资源的代码段。在临界区内,对共享资源的访问需要进行互斥,以确保同一时刻只有一个线程能够进入临界区

  3. 互斥与原子性
    互斥: 互斥是一种机制,确保在任何时刻只有一个线程能够进入临界区,从而访问临界资源,通常对临界资源起保护作用。
    原子性: 表示一个操作是不可中断的,要么全部执行成功,要么完全不执行。在多线程环境中,原子操作是不会被其他线程中断的操作,确保原子性可以避免竞态条件和数据不一致性。

示例程序说明(不使用互斥量):
在这里插入图片描述
上述程序输出结果表明,由于多个线程同时访问和修改共享资源(available_tickets)而没有同步机制,导致竞态条件的发生。在输出中,票数递减,并最终变成负数。由于线程执行的顺序不确定,每次运行结果可能不同。

  1. 互斥量和加锁

在这里插入图片描述

  1. pthread_mutex_t 类型
    pthread_mutex_tPOSIX 线程库提供的互斥量类型,用于在多线程环境中同步对共享资源的访问。它是一个结构体类型,通常通过指针进行操作
  2. pthread_mutex_init 函数
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
						const pthread_mutexattr_t *restrict attr);
  1. pthread_mutex_destroy 函数
#include <pthread.h>
 int pthread_mutex_destroy(pthread_mutex_t *mutex); 
  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_MUTEX_INITIALIZER 是一个宏,用于静态初始化一个互斥锁(mutex)。
在定义互斥量时,可以使用以下宏进行初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  1. pthread_mutex_lock 函数
 #include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
  1. pthread_mutex_unlock 函数
 #include <pthread.h>
 int pthread_mutex_unlock(pthread_mutex_t *mutex);

示例程序说明(使用互斥量):
在这里插入图片描述

以上程序通过互斥量确保了多线程环境下对共享资源(票数)的安全访问,实现了线程安全的抢票操作。

3. 线程同步与竞态条件

在多线程编程中,多个线程共享进程的资源,这样就可能导致竞态条件的产生。竞态条件是由于多个线程对共享资源的访问顺序不确定而引起的问题。为了解决这一问题,引入了线程同步的概念。

程序示例(不使用线程同步)
在这里插入图片描述

4. pthread库与条件变量

在多线程编程中,线程之间的协调与同步是至关重要的。pthread库提供了一系列工具,其中条件变量(pthread_cond_t)是一种强大的机制。

在这里插入图片描述

  1. pthread_cond_t类型
    pthread_cond_tpthread库中用于线程同步的一种机制,也被称为条件变量条件变量允许线程在等待某个条件满足时进入休眠状态,并在条件发生变化时被唤醒。条件变量通常与互斥锁一起使用,以确保多个线程对共享资源的安全访问。

  2. pthread_cond_init函数

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, 
					  const pthread_condattr_t *restrict attr);
  1. pthread_cond_destroy 函数
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
  1. PTHREAD_MUTEX_INITIALIZER

PTHREAD_COND_INITIALIZER宏,是一种静态初始化pthread_cond_t类型的条件变量的方式。
在定义条件变量时,可以使用以下宏进行初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  1. pthread_cond_wait函数
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  1. pthread_cond_broadcast函数
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
  1. pthread_cond_signal函数
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);

程序示例(使用线程同步)
在这里插入图片描述

5. 生产者-消费者

  1. 生产者消费者模型的概念

在这里插入图片描述

生产者消费者模型通过中间容器(阻塞队列)解决了生产者和消费者强耦合的问题。两者不直接通讯,而是通过阻塞队列传递数据。生产者产生数据后,无需等待消费者处理,直接放入阻塞队列。消费者不主动请求数据,而是直接从阻塞队列取出,阻塞队列充当了缓冲区的角色。

  1. 生产者、消费者之间的关系

在生产者消费者模型中存在三种关系:

生产者和生产者之间的互斥关系: 确保不同生产者之间的操作互斥,以保证数据的正确生成。

消费者和消费者之间的互斥关系: 确保不同消费者之间的操作互斥,防止并发访问导致问题。

生产者和消费者之间的互斥与同步关系: 实现互斥以保证读写安全,同时在缓冲区数据满或空时,确保能够互相等待和通知,实现同步操作。生产者线程和消费者线程是两种不同的角色,彼此之间通过缓冲区进行数据交流,这个缓冲区可被看作是一个特定结构的交换平台。

代码示例:

在这里插入图片描述

这个程序展示了基本的生产者-消费者模型,通过互斥锁和条件变量确保线程安全的访问共享资源。生产者不断生成随机数作为数据项并放入缓冲区,而消费者则从缓冲区中取出数据项进行消费。这种模型确保了在多线程环境中的安全数据传递和协同工作。

举报

相关推荐

0 条评论