首页 > 系统相关 >Linux系统编程之线程

Linux系统编程之线程

时间:2024-06-01 15:59:27浏览次数:25  
标签:Linux int void 编程 mutex pthread 线程 arg

一.线程介绍

1.进程与线程

  典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。  

  进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。

  线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程包含了表示进程内执行环境必须的信息,其中包括进程中表示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno常量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。在Unix和类Unix操作系统中线程也被称为轻量级进程(lightweight processes),但轻量级进程更多指的是内核线程(kernel thread),而把用户线程(user thread)称为线程。

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

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

2.使用线程的理由

 从上面我们知道了进程与线程的区别,其实这些区别也就是我们使用线程的理由。总的来说就是:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

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

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

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

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

二.线程相关函数

1.线程创建函数

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
返回值:成功返回0,失败返回错误编号

pthread_t *thread:线程id

const pthread_attr_t *attr:线程属性,一般用NULL,即默认属性

void *(*start_routine) (void *):线程执行的函数

void *arg:函数参数,若有多个参数,则需要构建一个结构体后传入结构体地址

2.线程退出函数

void pthread_exit(void *retval);

void *retval:rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针,其传递的参数若为函数中的局部变量需为static静态变量,否则线程退出后,其资源会被释放,导致pthread_join访问到的数据为乱码

3.线程等待函数

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

返回值:成功返回0,失败返回错误编号

在运行到该函数时,若线程id对应的线程未退出,则运行该函数的线程被阻塞,直到对应线程退出时结束阻塞

4.获取线程id

pthread_t pthread_self(void);

5.示例

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

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);
//void pthread_exit(void *retval);

int g_data = 0;

void *func1(void *arg)
{
        static char *ret = "thread exit";

        printf("t1:%ld,thread is created\n",(long int)pthread_self());
        printf("t1:arg = %d\n",*(int *)arg);

        while(1){
                printf("t1:%d\n",g_data++);
                printf("t1:arg = %d\n",*(int *)arg);
                sleep(5);
        }

        pthread_exit((void *)ret);
}

void *func2(void *arg)
{
        static char *ret = "thread exit";

        printf("t2:%ld,thread is created\n",(long int)pthread_self());
        printf("t2:arg = %d\n",*(int *)arg);

        while(1){
                printf("t2:%d\n",g_data++);
                printf("t2:arg = %d\n",*(int *)arg);
                sleep(1);
        }

        pthread_exit((void *)ret);
}

int main()
{
        pthread_t t1;
        pthread_t t2;
        int arg = 100;
        int ret;
        char *pret = NULL;

        ret = pthread_create(&t1, NULL, func1, (void *)&arg);
        ret = pthread_create(&t2, NULL, func2, (void *)&arg);
        if(ret == 0){
                printf("main:create thread success\n");
        }else{
                exit(-1);
        }
        printf("main:arg = %d\n",arg);

        while(1){
                printf("main:%d\n",g_data++);
                sleep(1);
        }

        pthread_join(t1,(void **)&pret);
        pthread_join(t2,(void**)&pret);
        printf("%s\n",pret);

        return 0;
}

三.互斥锁相关函数

1.互斥锁创建函数

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);

返回值:成功返回0,失败返回错误编号
pthread_mutex_t *restrict mutex:互斥锁名

 const pthread_mutexattr_t *restrict attr:互斥锁属性,一般用NULL,即默认属性

2.互斥锁销毁函数

int pthread_mutex_destroy(pthread_mutex_t *mutex);

3.互斥锁操作函数(加锁,解锁)

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY

如果一个线程拿到了互斥锁,则在同一个互斥锁上的其他线程在运行 pthread_mutex_lock函数时会被阻塞,直到拿到了互斥锁的线程运行pthread_mutex_unlock进行解锁,被阻塞的其他线程才会继续运行,如果有多个线程在同一个互斥锁上,则先竞争拿到该互斥锁的线程运行,其他线程被阻塞

4.示例

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

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);
//void pthread_exit(void *retval);

int g_data = 0;
pthread_mutex_t mutex;

void *func1(void *arg)
{
        static char *ret = "thread exit";

        printf("t1:%ld,thread is created\n",(long int)pthread_self());
        printf("t1:arg = %d\n",*(int *)arg);

        pthread_mutex_lock(&mutex);

        while(1){
                printf("t1:%d\n",g_data++);
                sleep(1);

                if(g_data = 3){
                        pthread_mutex_unlock(&mutex);
                        printf("t1 quit===============================================\n");
                        pthread_exit((void *)ret);
                }
        }

        pthread_exit((void *)ret);
}

void *func2(void *arg)
{
        static char *ret = "thread exit";

        printf("t2:%ld,thread is created\n",(long int)pthread_self());
        printf("t2:arg = %d\n",*(int *)arg);

        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2:%d\n",g_data++);
                pthread_mutex_unlock(&mutex);
                sleep(1);
        }

        pthread_exit((void *)ret);
}

int main()
{
        pthread_t t1;
        pthread_t t2;
        int arg = 100;
        int ret;
        char *pret = NULL;

        pthread_mutex_init(&mutex,NULL);

        ret = pthread_create(&t1, NULL, func1, (void *)&arg);
        if(ret == 0){
                printf("main:create thread t1 success\n");
        }else{
                exit(-1);
        }
        ret = pthread_create(&t2, NULL, func2, (void *)&arg);
        if(ret == 0){
                printf("main:create thread t2 success\n");
        }else{
                exit(-1);
        }

        printf("main:arg = %d\n",arg);

        while(1){
                printf("main:%d\n",g_data++);
                sleep(1);
        }

        pthread_join(t1,(void **)&pret);
        pthread_join(t2,(void**)&pret);

        pthread_mutex_destroy(&mutex);

        printf("%s\n",pret);

        return 0;
}

5.死锁

发生在有两个互斥锁及以上的程序,以两个互斥锁为例,当线程1拿到互斥锁1时又想要去拿到互斥锁2,但是互斥锁2已被线程2拿到并且线程2想要拿到互斥锁1则会发生死锁

void *func1(void *arg)
{
        static char *ret = "thread exit";

        printf("t1:%ld,thread is created\n",(long int)pthread_self());
        printf("t1:arg = %d\n",*(int *)arg);

        pthread_mutex_lock(&mutex);
        sleep(1);
        pthread_mutex_lock(&mutex2);

        while(1){
                printf("t1:%d\n",g_data++);
                sleep(1);

                if(g_data = 3){
                        pthread_mutex_unlock(&mutex);
                        printf("t1 quit===============================================\n");
                        pthread_exit((void *)ret);
                }
        }

        pthread_exit((void *)ret);
}

void *func2(void *arg)
{
        static char *ret = "thread exit";

        printf("t2:%ld,thread is created\n",(long int)pthread_self());
        printf("t2:arg = %d\n",*(int *)arg);

        pthread_mutex_lock(&mutex2);
        sleep(1);
        pthread_mutex_lock(&mutex);

        while(1){
                printf("t2:%d\n",g_data++);
                sleep(1);
        }

        pthread_exit((void *)ret);
}

四.条件相关函数

1.条件创建函数

int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);

参数介绍及返回值同互斥锁创建函数

动态创建是运用函数

静态创建是运用宏

静态创建示例:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

互斥锁的静态创建同条件的静态创建

2.条件销毁函数

int pthread_cond_destroy(pthread_cond_t *cond);

3.条件等待函数

int pthread_cond_timedwait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex,
           const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);
当线程运行到该函数时,会等待指定互斥锁上的指定条件触发

4.条件触发函数

int pthread_cond_signal(pthread_cond_t *cond);//触发

int pthread_cond_broadcast(pthread_cond_t *cond);//广播

这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。注意一定要在改变条件状态以后再给线程发信号

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

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//                          void *(*start_routine) (void *), void *arg);
//int pthread_join(pthread_t thread, void **retval);
//void pthread_exit(void *retval);

int g_data = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;

/*静态创建
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
*/

void *func1(void *arg)
{
        static char *ret = "thread exit";

        printf("t1:%ld,thread is created\n",(long int)pthread_self());
        printf("t1:arg = %d\n",*(int *)arg);

        while(1){
                pthread_cond_wait(&cond,&mutex);
                printf("t1 run===============================================\n");
                printf("t1:%d\n",g_data);
                g_data = 0;
        }

        pthread_exit((void *)ret);
}

void *func2(void *arg)
{
        static char *ret = "thread exit";

        printf("t2:%ld,thread is created\n",(long int)pthread_self());
        printf("t2:arg = %d\n",*(int *)arg);

        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2:%d\n",g_data++);
                pthread_mutex_unlock(&mutex);
                sleep(1);
                if(g_data == 3){
                        pthread_cond_signal(&cond);
                }
        }

        pthread_exit((void *)ret);
}

int main()
{
        pthread_t t1;
        pthread_t t2;
        int arg = 100;
        int ret;
        char *pret = NULL;

        pthread_mutex_init(&mutex,NULL);
        pthread_cond_init(&cond,NULL);

        ret = pthread_create(&t1, NULL, func1, (void *)&arg);
        if(ret == 0){
        //      printf("main:create thread t1 success\n");
        }else{
                exit(-1);
        }
        ret = pthread_create(&t2, NULL, func2, (void *)&arg);
        if(ret == 0){
        //      printf("main:create thread t2 success\n");
        }else{
                exit(-1);
        }

        printf("main:arg = %d\n",arg);

        pthread_join(t1,(void **)&pret);
        pthread_join(t2,(void**)&pret);

        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);

        printf("%s\n",pret);

        return 0;
}

标签:Linux,int,void,编程,mutex,pthread,线程,arg
From: https://blog.csdn.net/2303_77402228/article/details/139374324

相关文章

  • Linux系统编程之进程间通信(IPC)
    一.进程间通信概述进程间通信(IPC,InterProcessCommunication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Streams支持不同主机上的两个进程IPC二.管道1.匿名管道(1)特点1......
  • Java中的网络编程:构建稳健的分布式应用
            网络编程是Java开发中至关重要的一部分,特别是在构建分布式系统和网络应用程序时。Java提供了丰富的网络编程API和库,使开发者能够轻松创建各种网络应用。本文将介绍Java中的网络编程基础、常用的网络通信协议、以及如何利用Java构建稳健的分布式应用。####1.......
  • Java多线程编程:提高程序性能与响应性
            多线程编程是利用计算机的多核心优势来提高程序的性能和响应性的重要手段之一。在Java中,通过多线程可以实现同时执行多个任务,充分利用CPU资源,加速程序的运行。本文将深入探讨Java多线程编程的基本概念、常用类库、并发问题以及最佳实践。####1.多线程基础概......
  • (中文参数)可编程逻辑IC 5SGXEB6R2F40I2G、5SGXEB6R2F40I3G、5SGXEB6R3F40I3G、5SGXEB6R
    概述StratixV是业内第一款可提供精度可变DSP模块的FPGA,这使得它可提供业内效率最高、性能最好的多精度DSP数据通路和功能,如FFT、FIR和浮点DSP。StratixVFPGA具有1.6Tbps串行交换能力,采用各种创新技术和前沿28-nm工艺,突破带宽瓶颈,降低了宽带应用的成本和功耗。StratixVFP......
  • GraalVM - Java8 Linux AMD64
    使用GraalVM在linuxamd64环境下编译Java8程序的步骤主要包括:下载GraalVM下载native-image安装native-image编译程序1.下载GraalVM可以通过Github的release页面直接下载(往回找,找到支持java8的graalvm-ce-java8-linux-amd64-20.3.2.tar.gz)https://github.com/graalvm/gra......
  • Linux文本文件管理003
    ★排序、去重、统计★1)排序sort-n按照数值排序-r降序排列2)去重uniq过滤相邻、重复的行-c对重复行计数3)统计wc统计文件中的字节数、单词数、行数-l显示行数今天通过使用grep、awk、cut指令和上面几个选项提取文本文件的信息方法1:[root@localhostnginx]#ca......
  • Linux进程管理
    在Linux系统中,进程管理是一个重要的任务。以下是一些常用的进程管理指令总结:1.ps:显示当前系统中的进程信息。可以使用psaux来查看所有用户的进程信息,包括详细的状态、CPU使用率等。psaux2.top:实时显示系统中的进程信息,包括进程ID、用户、CPU使用率、内存使用情况等......
  • 总结常用9种下载(限速、多线程加速、ZIP、导Excel)
    一、前言下载文件在我们项目很常见,有下载视频、文件、图片、附件、导出Excel、导出Zip压缩文件等等,这里我对常见的下载做个简单的总结,主要有文件下载、限速下载、多文件打包下载、URL文件打包下载、Excel导出下载、Excel批量导出Zip包下载、多线程加速下载。二、搭建SpringBoo......
  • 【Linux】如何利用linux项目自动化构建工具-make/Makefile以及vim编辑器构建两个小程
    1.倒计时小程序首先我们Linux中创建目录test1,该目录中包含了makefile文件,和main.c文件(该文件是源文件用于编写倒计时程序的代码)再进行依赖方法和依赖关系的确定: 利用vim编辑器编辑makefile文件:注意:在依赖方法前面加@的作用是,执行make指令后,将对应的依赖方法不显示在屏幕......
  • Linux基础 (九):Linux 进程复制与替换
       各位看官,本篇博客干货满满,请耐下心来,慢慢吸收!哈哈哈,内功一定会大增!目录一、printf函数输出问题1.1第1个示例代码1.2第2个示例代码1.3分析与结论二、主函数参数介绍三、复制进程fork3.1进程的基本概念3.2fork()方法3.3fork方法使用示例3.4 面试题fo......