Posix多线程编程

简介: Posix多线程编程

一、线程与多线程的定义

线程存在于进程当中,是操作系统调度执行的最小单位。说通俗点线程就是干活,多线程也就是同时可以干不同的活而且还不会互相打扰,线程并没有自己的独立空间。

二、进程与线程的区别与联系

    如果说进程是一个资源管家,负责从主人那里要资源的话,那么线程就是干活的苦力。

一个管家必须完成一项工作,就需要最少一个苦力,也就是说,一个进程最少包含一个线程,也可以包含多个线程。苦力要干活,就需要依托于管家,所以说一个线程,必须属于某一个进程。进程有自己的地址空间,线程使用进程的地址空间,也就是说,进程里的资源,线程都是有权访问的,比如说堆啊,栈啊,静态存储区什么的。

线程就是个无产阶级,但无产阶级干活,总得有自己的劳动工具吧,这个劳动工具就是栈,线程有自己的栈,这个栈仍然是使用进程的地址空间,只是这块空间被线程标记为了栈。每个线程都会有自己私有的栈,这个栈是不可以被其他线程所访问的。

从上面我们知道了进程和线程区别,使用多线程首先是要和进程相对比,它是一种非常便捷的多任务操作方式;我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

线程操作相关的函数:

(1)线程创建函数

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);

参数说明:

pthread_create用于创建一个线程,成功返回0,否则返回Exxx(为正数)。

pthread_t *tid:线程id的类型为pthread_t,通常为无符号整型,当调用pthread_create成功时,通过*tid指针返回。

const pthread_attr_t *attr:指定创建线程的属性,如线程优先级、初始栈大小、是否为守护进程等。可以使用NULL来使用默认值,通常情况下我们都是使用默认值。

void *(*func) (void *):函数指针func,指定当新的线程创建之后,将执行的函数。

void *arg:线程将执行的函数的参数。如果想传递多个参数,请将它们封装在一个结构体中。

(2)线程等待的函数

int pthread_join (pthread_t tid, void ** status);

参数说明:

pthread_join用于等待某个线程退出,成功返回0,否则返回Exxx(为正数)。

pthread_t tid:指定要等待的线程ID

void ** status:如果不为NULL,那么线程的返回值存储在status指向的空间中(这就是为什么status是二级指针的原因!这种才参数也称为“值-结果”参数)。

(3)获得线程自身的ID的函数

pthread_t pthread_self (void);

pthread_self用于返回当前线程的ID

(4)线程分离的函数

int pthread_detach (pthread_t tid);

pthread_detach用于是指定线程变为分离状态,就像进程脱离终端而变为后台进程类似。成功返回0,否则返回Exxx(为正数)。变为分离状态的线程,如果线程退出,它的所有资源将全部释放。而如果不是分离状态,线程必须保留它的线程ID,退出状态直到其它线程对它调用了pthread_join

(5)退出线程(终止线程)的函数

void pthread_exit (void *status);

pthread_exit用于终止线程,可以指定返回值,以便其他线程通过pthread_join函数获取该线程的返回值。

参数说明:

void *status:指针线程终止的返回值。

Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上弥补了这一缺陷。目前最为流行的线程机制LinuxThreads所采用的就是线程-进程“一对一”模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。

线程编程实例:pthread.c

#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <pthread.h>
 void *thread_1(void *arg)
 {
     printf("thread one\n");
 }
void *thread_2(void *arg)
{
    printf("thread two\n");
}
int main()
{
    int ret = 0;
    pthread_t pid = 0;
pthread_t pid1 = 0;
    //创建线程1
    ret = pthread_create(&pid, NULL, thread_1, NULL);
    if(ret < 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    //创建线程2
    ret = pthread_create(&pid1, NULL, thread_2, NULL);
    if(ret < 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    //等待线程退出
    pthread_join(pid, NULL);
    pthread_join(pid1, NULL);
    return 0;
}

注意,在gcc中,默认是不包含线程相关的库的,所以在编译这个程序操作如下是会产生错误的,如图4-3-25所示。

640.jpg

       图4-3-25 gcc编译中没有包含线程库的验证结果

 正确的编译方式是下面这样,要加上-lpthread这个库,确保编译的时候链接上。如图4-3-26所示

640.png

           图4-3-26 创建线程

运行结果,如图4-3-27所示。

640.png

           图4-3-27 创建线程的实验结果

pthread.c创建了2个线程,并在线程中实现打印功能,最终调用pthread_join等待子线程运行结束,一并退出。

通过上面的一个例程会发现一个问题,当我们去操作一个文件的时候会出现一个问题,就是不知道该听谁的,这时候我们就需要一个互斥锁,让线程一个个来,a执行完之后在执行b

互斥锁例程:pthread2.c

#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <pthread.h>
 //定义一个互斥锁变量
 pthread_mutex_t m;
 void *thread_1(void *arg)
 {
    //互斥锁加锁
    pthread_mutex_lock(&m);
    printf("thread one\n");
    //互斥锁解锁
   pthread_mutex_unlock(&m);
}
void *thread_2(void *arg)
{
    pthread_mutex_lock(&m);
    printf("thread two\n");
    pthread_mutex_unlock(&m);
}
int main()
{
    int ret = 0;
    //以动态方式创建互斥锁
    ret = pthread_mutex_init(&m, NULL);
    if(ret < 0)
    {
        perror("pthread_mutex_init");
        exit(EXIT_FAILURE);
    }
    pthread_t pid = 0;
    pthread_t pid1 = 0;
    ret = pthread_create(&pid, NULL, thread_1, NULL);
   if(ret < 0)
   {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    ret = pthread_create(&pid1, NULL, thread_2, NULL);
   if(ret < 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    pthread_join(pid, NULL);
    pthread_join(pid1, NULL);
    return 0;
}

     一样的,同样执行编译加上-lpthread参数保证编译时链接线程库,然后运行,如图4-3-28所示。

640.png

                               图4-3-28 添加互斥锁测试

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该函数的某个数据时,进行保护,其它线程不能进行访问直到该线程读取完成,其它线程才可以使用。不会出现数据不一致或者数据污染。

目录
相关文章
|
3天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
2天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。
|
3天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
18 3
|
5天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
6天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
11 3
|
6天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
9 2
|
6天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
18 2
|
6天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
15 2
|
6天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
11 1
|
6天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
12 1