Linux应用编程---5.多线程的创建以及线程间数据共享

Linux应用编程—5.多线程的创建以及线程间数据共享

5.1 多线程的创建

​ 创建多线程,则多次调用pthread_create()函数。创建两个线程,线程1每隔一秒打印字符串:Hello world!,线程2每隔一秒打印字符串:Good moring!编写代码如下:

include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void * pthread1_function(void *arg);
void * pthread2_function(void *arg);

int main(void)
{
        pthread_t pthread_1, pthread_2;
        int ret = 0;

        ret |= pthread_create(&pthread_1, NULL, pthread1_function, NULL);

        if(0 != ret)
        {
                perror("pthread create.");
                exit(1);
        }

        ret |= pthread_create(&pthread_2, NULL, pthread2_function, NULL);

        if(0 != ret)
        {
                perror("pthread create.");
                exit(1);
        }

        pthread_join(pthread_1, NULL);
        pthread_join(pthread_2, NULL);

        return 0;
}

void * pthread1_function(void *arg)
{
        printf("Pthread1 start running.\n");

        while(1)
        {
                printf("Hello world!\n");
                sleep(1);
        }
    
        return NULL;
}

void * pthread2_function(void *arg)
{
        printf("Pthread2 start running.\n");

        while(1)
        {
                printf("Good moring.\n");
                sleep(1);
        }

        return NULL;
}

​ 运行结果:

image-20221124103959720

图1 代码运行结果
Pthread1 start running.
Hello world!
Pthread2 start running.
Good moring.
Hello world!
Good moring.
Hello world!
Good moring.
Good moring.
Hello world!
Hello world!
Good moring.
Good moring.
Hello world!
Hello world!
Good moring.
Good moring.
Hello world!
Hello world!
Good moring.
Good moring.
Hello world!
Hello world!
Good moring.
^C

​ 可以看出两个线程内的代码同时运行,看上去Hello world!与Good moring.每隔一秒,同时打印出来。

5.2 多线程之间数据共享

​ 定义一个全局变量,分别在两个线程中对其进行++操作以及打印结果。回顾之前父子进程对同一个变量的操作,父子进程在内存中是互相独立的,所以,他们分别增加,两者互相不影响。编写代码测试在线程中对一个全局变量的操作会有什么现象。

#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void * pthread1_function(void *arg);
void * pthread2_function(void *arg);

int temp = 0;			//定义了全局变量,在线程中对其操作。

int main(void)
{
        pthread_t pthread_1, pthread_2;
        int ret = 0;

        ret |= pthread_create(&pthread_1, NULL, pthread1_function, NULL);

        if(0 != ret)
        {
                perror("pthread create.");
                exit(1);
        }

        ret |= pthread_create(&pthread_2, NULL, pthread2_function, NULL);

        if(0 != ret)
        {
                perror("pthread create.");
                exit(1);
        }

        pthread_join(pthread_1, NULL);
        pthread_join(pthread_2, NULL);

        return 0;
}

void * pthread1_function(void *arg)
{
        printf("Pthread1 start running.\n");

        while(1)
        {
                printf("In pthread1_function, temp = %d.\n", temp++);
                sleep(1);
        }

        return NULL;
}

void * pthread2_function(void *arg)
{
        printf("Pthread2 start running.\n");

        while(1)
        {
                printf("In pthread2_function, temp = %d.\n", temp++);
                sleep(1);
        }

        return NULL;
}

​ 运行结果:

image-20221124215142198

图2 代码运行结果

​ 运行结果可见,对这两个线程来说,temp是同一个变量,第一次,在线程1中为0,在线程2中+1为1;第二次在线程1中+1为2,在线程2中+1为3,依次下去。这是因为对于线程来说,资源是共享的,这一点与进程中的父子进程是有区别的。线程之间资源是共享的,所以实现线程之间的通讯比进程之间通讯要简单。

疑问?如果说多线程是并发执行的,但是temp变量是依次打印的,是不是可以说明线程之间执行有先后顺序呢?

5.3 总结

​ 本次了解了多线程的创建方式,本质还是通过多次调用线程创建函数实现的。线程之间资源贡献,所以对于一个全局变量在多线程之间是可见的,通过这个特点,多线程也更好实现线程之间的通讯。
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程概念 什么是线程 LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空,拥有PCB 线程:也有PCB,但没有独立的地址空(共享) 区别:在于是否共享地址空。 独居(进程);合租(线程)。 Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程。 Linux内核线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。 1. 轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone 2. 从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的 3. 进程可以蜕变成线程 4. 线程可看做寄存器和栈的集合 5. 在linux下,线程最是小的执行单位;进程是最小的分配资源单位 察看LWP号:ps –Lf pid 查看指定线程的lwp号。 三级映射:进程PCB --> 页目录(可看成数组,首地址位于PCB中) --> 页表 --> 物理页面 --> 内存单元 参考:《Linux内核源代码情景分析》 ----毛德 对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。 但!线程不同!两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空。 实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数clone。 如果复制对方的地址空,那么就产出一个“进程”;如果共享对方的地址空,就产生一个“线程”。 因此:Linux内核是不区分进程和线程的。只在用户层面上进行区分。所以,线程所有函数 pthread_* 是库函数,而非系统调用。 线程共享资源 1.文件描述符表 2.每种信号的处理方式 3.当前工目录 4.用户ID和组ID 5.内存地址空 (.text/.data/.bss/heap/共享库) 线程共享资源 1.线程id 2.处理器现场和栈指针(内核栈) 3.独立的栈空(用户空栈) 4.errno变量 5.信号屏蔽字 6.调度优先级 线程优、缺点 优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便 缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好 优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。 线程控制原语 pthread_self函数 获取线程ID。其用对应进程中 getpid() 函数。 pthread_t pthread_self(void); 返回值:成功:0; 失败:无! 线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu),其他系统中可能是结构体实现 线程ID是进程内部,识别标志。(两个进程线程ID允许相同) 注意:不应使用全局变量 pthread_t tid,在线程中通过pthread_create传出参数来获取线程ID,而应使用pthread_self。 pthread_create函数 创建一个新线程。 其用,对应进程中fork() 函数。 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。 参数: pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t; 参数1:传出参数,保存系统为我们分配好的线程ID 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。 参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。 参数4:线程主函数执行期所使用的参数。 在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。star

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值