Linux线程

文章目录

一、线程与进程

1.1.线程的介绍

典型的 UNIX/Linux 进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。进程是程序执行时的一个实例,是担当分配系统资源(CPU 时间、内存等)的基本单位。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。线程是操作系统能够进行运算调度的最小单位。 总结:进程------资源分配的最小单位,线程------程序执行的最小单位。

1.2.线程与进程的关系

在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器,线程相当于寄生在进程里,一个进程至少有一个线程,进程有独立的地址空间。当一个进程崩溃后,在保护模式下不会对其它进程产生影响,而在进程下的线程会随着该进程崩溃。

下面表格是线程与进程的对比:

操作 进程开销 线程开销
创建时间 ~1ms ~0.01ms
上下文切换 较重 很轻
内存占用 独立地址空间 共享地址空间
通信开销 需要内核介入 直接内存访问

线程与进程的内存空间关系,在同一个进程的线程中,所有线程共享进程的代码段、数据段、堆、BSS 段(Block Started by Symbol专门用于存放未初始化的全局变量和静态变量),每个线程都有自己的栈区,用于存放变量和函数调用等。

二、关于多线程的 API 函数

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,面对操作线程的 API 有创建、退出和等待操作;互斥锁是面对保护资源的操作有创建、销毁、加锁和解锁;条件变量是面对线程间交流的操作有创建、销毁、触发和广播,共 12 个函数:

2.1.线程

面对线程有创建、退出和销毁三个函数,下面是创建线程函数原型:

c 复制代码
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, 				//定义一个pthread_t 类型的线程ID,并把它的地址传进来
				   const pthread_attr_t *restrict attr, 	//定义该线程属性,比如它的栈大小、优先级,一般写NULL设置默认
				   void *(*start_rtn)(void *), 				//编写一个这个新线程做事情的函数,并把函数名传进来
				   void *restrict arg);						//传参数给新线程的函数里
//返回值:若成功返回0,否则返回错误编号

退出进程有多种方法,

  • 线程只是从启动例程中返回,返回值是线程的退出码
  • 线程可以被同一进程中的其他线程取消
  • 线程调用pthread_exit:

rval_ptr 是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用 pthread_join 函数访问到这个指针。下面是该函数的原型:

c 复制代码
#include <pthread.h>
int pthread_exit(void *rval_ptr);	//这是线程结束时要传递出去的返回值,它可以指向任何类型的数据
									//什么都不传就填NULL	

线程等待,如果不调用该函数等待线程完成,主函数就早早结束进程,会导致僵尸线程的诞生,严重浪费资源,因此,当主函数完成了自己的工作,需要调用该函数等待线程的工作完成才能结束整个进程,下面是该函数的原型:

c 复制代码
#include <pthread.h>
int pthread_join(pthread_t thread,	//线程ID 
				 void **rval_ptr);	//需要另外定义一个变量来承接线程函数所返回的数据,void **是固定格式主要是能承接各种类型的数据

获取线程 ID 的函数:

c 复制代码
#include <pthread.h>
pthread_t pthread_self(void);	//返回值是线程ID

下面是各个 API 函数的综合应用,将主线程和新线程的值打印出来,该程序的输出结果可能先运行新线程再是主线程,它们的顺序是不定的,会相互抢夺资源:

c 复制代码
#include <stdio.h>
#include <pthread.h>

void *t1WorkPlace(void *arg)
{
    static int ret;

    ret = *(int *)arg + 10;
    printf("T1:This is new thread:%ld\n", (unsigned long)pthread_self());
    printf("T1:arg = %d\n", *(int *)arg);

    pthread_exit((void *)&ret);
}

int main()
{
    pthread_t threadID;
    int arg = 250;
    int ret;
    int *getRet = NULL;

    ret = pthread_create(&threadID, NULL, t1WorkPlace, (void *)&arg);
    if(ret == 0)
    {
            printf("Create thread success\n");
    }


    pthread_join(threadID, (void **)&getRet);
    printf("Get value from new thread is:%d\n", *getRet);
    printf("Main thread:%ld\n", pthread_self());

    return 0;
}

2.2.互斥量

互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。为了防止一个全局变量被一个线程修改的过程中,被另一个线程打断,进而导致数据的错误,所以需要一把锁保护数据,这样有序的整改同一个变量,数据的准确性就提高了。下面是创建和销毁互斥量的函数:

c 复制代码
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, 		 //需要定义一个pthread_mutex_t类型的变量为锁的ID
					   const pthread_mutexattr_t *restrict attr);//定义锁的属性,例如:普通锁、递归锁、检错锁等,填NULL用默认属性
int pthread_mutex_destroy(pthread_mutex_t mutex);	//互斥锁的ID
//返回值:若成功返回0,否则返回错误编号

下面是加锁与解锁的函数原型:

c 复制代码
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t mutex);		//锁的ID
int pthread_mutex_unlock(pthread_mutex_t mutex);
//返回值:若成功返回0,否则返回错误编号

下面例子:创建两个新的线程,每个线程都有自己的工作,新的两个线程加上锁,必须等待某一个线程运行完成才轮到另一个线程,两个新线程运行的过程中可能被主线程打断,因为主线程并没有互斥锁保护:

c 复制代码
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;

void *t1Fun(void *arg)
{
    int i;
    pthread_mutex_lock(&mutex);
    for(i = 0; i < 5; i++)
    {
            printf("T1:%ld---------------------------------------------\n", pthread_self());
            printf("T1:Get data From main:%d\n", *(int *)arg);
            sleep(1);
    }
    pthread_mutex_unlock(&mutex);
}

void *t2Fun(void *arg)
{
    pthread_mutex_lock(&mutex);
    printf("T2:%ld\n", pthread_self());
    printf("T2:Get data From main:%d\n", *(int *)arg);
    pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t t1, t2;
    int ret;
    int arg = 250;


    pthread_mutex_init(&mutex, NULL);

    ret = pthread_create(&t1, NULL, t1Fun, (void *)&arg);
    ret = pthread_create(&t2, NULL, t2Fun, (void *)&arg);
    if(ret == 0)
    {
            printf("Main:Create success\n");
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex);

    return 0;
}

2.3.条件变量

条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。下面是创建及销毁条件变量的函数:

c 复制代码
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,				//定义一个pthread_cond_t类型的条件变量
 				      const pthread_condattr_t *restrict attr);		//除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL
int pthread_cond_destroy(pthread_cond_t cond);		//填入定义的条件变量
// 返回:若成功返回0,否则返回错误编号

下面函数是等待条件的到来,只有某个线程给了合适的条件,就会进行该线程的操作:

c 复制代码
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, 	//定义一个pthread_cond_t类型的变量
					  pthread_mutex_t *restrict mutex);		//需要使用哪一把锁
// 返回:若成功返回0,否则返回错误编号

下面是触发条件的函数:

c 复制代码
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号

创建新的两个线程,这两个线程分别对全局变量 g_data 进行修改,每当 g_data 加到 5 的时候,就触发 t1 线程的条件变量,并对 g_data 重新赋值为 0,当 t1 线程的变量 i 为 2 的时候,退出整个进程:

c 复制代码
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int g_data = 0;

void *t1Fun(void *arg)
{
    int i = 0;
    printf("t1:%ld\n", pthread_self());
    while(1)
    {
            pthread_cond_wait(&cond, &mutex);
            printf("t1:-----------------R-----U-----N-----------------------------------\n");
            printf("t1:data=%d\n", g_data);
            g_data = 0;
            sleep(1);
            i++;
            printf("i = %d\n", i);
            if(i == 2)
            {
                    exit(1);
            }
    }
}

void *t2Fun(void *arg)
{
    printf("T2:Get data From main:%d\n", *(int *)arg);
    while(1)
    {
            pthread_mutex_lock(&mutex);
            printf("t2:data:%d\n", g_data);
            g_data++;
            if(g_data == 5)
            {
                    pthread_cond_signal(&cond);
            }
            sleep(1);
            pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    pthread_t t1, t2;
    int ret;
    int arg = 250;


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

    ret = pthread_create(&t1, NULL, t1Fun, (void *)&arg);
    ret = pthread_create(&t2, NULL, t2Fun, (void *)&arg);
    if(ret == 0)
    {
            printf("Main:Create success\n");
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}
相关推荐
搬砖的小码农_Sky4 小时前
Ubuntu Server 命令行关机指南
linux·运维·ubuntu
yangmf20404 小时前
如何使用证书认证连接 Easysearch
运维·elasticsearch·搜索引擎
zzzsde5 小时前
【Linux】基础指令(2):理解Linux的指令和核心概念
linux·运维·服务器
Empty_7775 小时前
Keepalived双机热备
linux·git·github
2501_938773875 小时前
从 0 到 1 搭建 TikTok 自动化系统:内容、投放、客服全环节自动化实践
运维·自动化
wanhengidc5 小时前
云手机 基于云计算的虚拟手机
运维·服务器·游戏·智能手机·云计算
塔能物联运维5 小时前
物联网设备固件版本智能管理与自动化更新策略
运维·物联网·自动化
凝新科技5 小时前
数字营销软件完整指南|CRM、CDP、自动化平台 2025
运维·自动化
小小的木头人6 小时前
Keepalived 多节点负载均衡配置
运维