Linux下多线程编程实例解析

时间:2022-12-16 18:31:24

提到线程,不得不让人想起进程,所以还是先写下进程与线程的区别吧!
一、进程与线程的区别
进程是程序执行的一个实例,进程有其自己独立的地址空间。一个线程可以含有多个线程,这也是为了提高系统资源利用率,线程的引入有其必然的优势,线程有自己的堆栈。记得操作系统的书籍上有一句经典的话:进程——资源分配的最小单位,线程——程序执行的最小单位。一个通俗的比喻:进程就像一个人,而线程就好像人体内协调工作的各个组织部分,比如血液循环,人的呼吸,心脏的跳动,他们协同工作,共用人体(进程独立的资源空间)。

二、线程操作的基本函数
1、创建线程函数

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void),
void *restrict arg);

成功创建返回0,返回其他值出错。
tidp:指向线程标识符的指针。
attr:设置线程属性。
start_trn:线程运行函数的起始地址。
arg:运行函数的参数。

2、线程属性函数

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);//对线程属性进行初始化。
int pthread_attr_destroy(pthread_attr_t *attr);//去除线程的初始化属性。

若成功返回0,失败返回-1。
attr:线程属性变量。
调用pthread_attr_init()函数后,attr中所包含的就是系统支持的线程所有属性的默认值。pthread_attr_destroy()去除attr中的属性。线程属性结构如下:
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
structsched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void* stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;

(1)线程的分离状态
有一个函数可以对线程的分离状态进行控制。

#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate);//获取线程的分离状态

int pthread_attr_setdetachstate(pthread_attr_t *attr,intdetachstate);//设置线程的分离状态

若成功返回0,若失败返回-1。
attr:线程属性变量。
detachstate,intdetachstate: 线程的分离状态属性。detachstate可以设置为PTHREAD_CREATE_DETACHED(分离状态启动线程),PTHREAD_CREATE_JOINABLE(正常启动线程)。

线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。

(2)线程的继承

#include <pthread.h>
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);//获取线程的继承性

int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);//设置线程的继承性

若成功返回0,若失败返回-1。
attr: 线程属性变量。
inheritsched: 线程的继承性。

继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。线程不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。
继承性的可能值是PTHREAD_INHERIT_SCHED(表示新线程将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果需要显式的设置一个线程的调度策略或参数,那么就必须在设置之前将inheritsched属性设置PTHREAD_EXPLICIT_SCHED。

(3)线程的调度策略

#include<pthread.h>
int pthread_attr_getschedpolicy(const pthread_attr_t*attr,int *policy);//获取线程的调度策略

int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);//设置线程的调度策略

若成功返回0,若失败返回-1。
attr:线程属性变量指针。
policy:调度策略。其中有如下调度策略。

SCHED_FIFO(先进先出):允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。
SCHED_RR(轮循):如果有一个SCHED_RR策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIFO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。
当有SCHED_FIFO或SCHED_RR策略的线程在一个条件变量上等待或等待加锁同一个互斥量时,它们将以优先级顺序被唤醒。即,如果一个低优先级的SCHED_FIFO线程和一个高优先级的SCHED_FIFO线程都在等待锁相同的互斥量,则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。

(4)线程的调度参数

#include<pthread.h>
int pthread_attr_getschedparam(const pthread_attr_t*attr,struct sched_param *param);
//获取线程的调度参数
int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param);
//设置线程的调度参数

若成功返回0,若失败返回-1。
attr: 线程属性变量指针。
param:sched_param结构。结构sched_param在文件/usr/include/bits/sched.h中定义如下:
struct sched_param
{
intsched_priority;
};
结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

(3) 等待其他线程退出函数

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

返回值 : 0代表成功。 失败,返回的则是错误号。
thread:线程ID。
retval: 用户定义的指针,用来存储被等待线程的返回值。
以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

三、实例分析
根据上述线程相关函数,有一个如下实例:

#include <string.h>
#include <semaphore.h>

#define MODULE_NAME_MAX_LEN 32 /* 定义模块名字最大长度 */

/*初始化优先级枚举*/
typedef enum
{
INIT_RIGHT_START,

INIT_TOP_RIGHT=1,
INIT_HIGH_RIGHT,
INIT_NORMAL_RIGHT,
INIT_LOW_RIGHT,

INIT_RIGHT_END,
}INIT_RIGHTS;

typedef void* (* PF_TASK_MAIN )(void* arg);
typedef unsigned long (* PF_INIT_FUNC )(unsigned long thread_id);

typedef struct ThreadCtl
{
unsigned long thread_id; /* 线程ID ,固定分配*/
char thread_name[MODULE_NAME_MAX_LEN]; /* 模块名 */
PF_INIT_FUNC init_func; /* 各个模块的初始化入口函数 */
unsigned long task_id; /* 模块任务ID,创建线程时,返回此值 */
unsigned long init_pri;/*模块的初始化优先级*/
unsigned long task_pri; /* 各个模块的任务优先级 */
int policy; /* 模块对应线程的调试策略*/
PF_TASK_MAIN task_func; /* 各个模块的任务入口函数 */
unsigned long task_para; /* 各个模块的任务入口参数 */

}thread_ctrl_t;

typedef enum
{
/*0模块ID预留*/
THREAD_ZERO_ID,
/*特殊模块号,主线程模块ID*/
THREAD_MAIN_ID,
/*在此处罗列模块枚举*/
THREAD_ONE_ID,
THREAD_TWO_ID,

/*模块结束ID*/
THREAD_END_ID,
}MODULE_IDS;

void init_main_thread_info(void);
void init_thread_list(void);
void init_module_manager(void);
int mod_if_register_thread(int thread_id,int init_right,int task_right,char * thread_name,
PF_INIT_FUNC init_func,PF_TASK_MAIN func_entry, int policy);
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <pthread.h>
#include "module.h"

using namespace std;
/* 在这里定义所有的应用模块,注意这里的数组需要和模块ID对应起来,方便管理*/
thread_ctrl_t thread_info[THREAD_END_ID+1];

/******将主线程的信息与其他线程信息一同管
**理在此函数中完成主线程信息的特殊初始化流程**************************************************/

void init_main_thread_info(void)
{
thread_info[THREAD_MAIN_ID].thread_id = THREAD_MAIN_ID;
strcpy(thread_info[THREAD_MAIN_ID].thread_name, "Main Thread");
thread_info[THREAD_MAIN_ID].init_func = NULL;
thread_info[THREAD_MAIN_ID].task_id = pthread_self();//获取自身线程的ID值
thread_info[THREAD_MAIN_ID].init_pri= INIT_TOP_RIGHT;
thread_info[THREAD_MAIN_ID].task_pri= 0;
thread_info[THREAD_MAIN_ID].task_func= NULL;
thread_info[THREAD_MAIN_ID].task_para= 0;
}

/***********显式初始化模块注册数组***************/
void init_thread_list(void)
{
int thread_id;
for(thread_id = THREAD_ZERO_ID;thread_id < THREAD_END_ID+1;thread_id++)
{
thread_info[thread_id].thread_id= 0;
strcpy(thread_info[thread_id].thread_name, "null");
thread_info[thread_id].init_func= NULL;
thread_info[thread_id].task_id= 0;
thread_info[thread_id].init_pri= 0;
thread_info[thread_id].task_pri= 0;
thread_info[thread_id].task_func= NULL;
thread_info[thread_id].task_para= 0;
}
}

/**********获取对应模块的线程优先级**************/
int mod_if_get_thread_priority(int thread_id)
{
int result;
result = thread_info[thread_id].task_pri;

return result;
}

/******************创建线程*********************/
int task_create(unsigned int thread_id, unsigned long* p_tidp, int policy, PF_TASK_MAIN func_entry,void * args)
{
if (p_tidp == NULL)
{
return -1;
}

int result;
pthread_attr_t pattr;
struct sched_param param;
pthread_attr_init(&pattr);
param.sched_priority = mod_if_get_thread_priority(thread_id);

pthread_attr_setschedpolicy(&pattr,policy);
pthread_attr_setschedparam(&pattr,&param);
pthread_attr_setinheritsched(&pattr,PTHREAD_EXPLICIT_SCHED);
result = pthread_create(p_tidp,
&pattr,
func_entry,
args);
pthread_attr_destroy(&pattr);
return result;
}

void startup_module(int thread_id)
{
task_create(thread_id, &thread_info[thread_id].task_id, thread_info[thread_id].policy, thread_info[thread_id].task_func, NULL);
}

/*按照模块注册时传入的初始化优先级进行模块初始化并创建线程*/
void startup_module_flowline(void)
{
int init_right = 0;
int moduleid = 0;
int init_result = 0;

for(init_right = INIT_TOP_RIGHT;init_right < INIT_RIGHT_END;init_right++)
{
for(moduleid = THREAD_ZERO_ID+1;moduleid < THREAD_END_ID;moduleid++)
{
if((thread_info[moduleid].init_pri== init_right)&&(moduleid != THREAD_MAIN_ID))
{
if(thread_info[moduleid].init_func != NULL)
{
init_result = thread_info[moduleid].init_func(moduleid);
}
startup_module(moduleid);
}
else
{
continue;
}
}
}
}

/****按照与启动时相反的顺序以阻塞主线程的方式等待线程结束*****/
void block_module_queue(void)
{
int init_right = 0;
int moduleid = 0;

for(init_right = INIT_RIGHT_END - 1; init_right > INIT_RIGHT_START; init_right--)
{
for(moduleid = THREAD_END_ID - 1; moduleid > THREAD_ZERO_ID; moduleid--)
{
if((thread_info[moduleid].init_pri== init_right)&&(moduleid != THREAD_MAIN_ID))
{
if(strcmp(thread_info[moduleid].thread_name,"null"))
{
pthread_join(thread_info[moduleid].task_id,NULL);
}
}
}
}
}

/***************模块管理的入口函数**********************/
void init_module_manager(void)
{
startup_module_flowline();
block_module_queue();
}

/******************************************************************
* 函数名: mod_if_register_module
* 功能: 模块注册函数
* 输入参数: thread_id:模块ID号
init_right:初始化优先级(模块启动顺序)
task_right:任务优先级(线程优先级 1~99)
thread_name:模块名
init_func:模块初始化入口(申请消息队列和信号量,初始化全局变量)
task_func:模块主函数入口
policy: 线程的调试策略(SCHED_FIFO, SCHED_OTHER, SCHED_RR)
******/

int mod_if_register_thread(int thread_id,int init_right,int task_right,char * thread_name,PF_INIT_FUNC init_func,PF_TASK_MAIN func_entry, int policy)
{
if(thread_id >= THREAD_END_ID)
{
printf("module error!!!\n");
return -1;
}
if((task_right < 1) ||(task_right > 99))
{
printf("The value of task priority should between 1 and 99!\n");
while(1);
}

if((thread_info[thread_id].thread_id == 0)&&(thread_info[thread_id].init_pri ==0)
&&(thread_info[thread_id].task_pri == 0)&&(strcmp(thread_info[thread_id].thread_name,"null") == 0)
&&(thread_info[thread_id].init_func == NULL)&&(thread_info[thread_id].task_func == NULL))
{
thread_info[thread_id].thread_id= thread_id;
thread_info[thread_id].init_pri= init_right;
thread_info[thread_id].task_pri= task_right;
strcpy(thread_info[thread_id].thread_name,thread_name);
thread_info[thread_id].init_func= init_func;
thread_info[thread_id].task_func= func_entry;
thread_info[thread_id].policy = policy;
}
else
{
printf("Registered module ID!\n");
while(1);
}

}
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <signal.h>
#include "module.h"

/*******************线程1执行的入口函数******/
void *thread_one_main(void* arg)
{
while(true)
{
sleep(3);
printf("MODULE ONE main func++++++++++++\n");
}
}

/*******************线程2执行的入口函数******/
void *thread_two_main(void* arg)
{
while(true)
{
printf("MODULE TWO main func------------\n");
sleep(3);
}
}
/*******************主函数main******************/
int main(void)
{
init_main_thread_info();
init_thread_list();

mod_if_register_thread(THREAD_ONE_ID,
INIT_TOP_RIGHT,
1,
const_cast<char *>("test thread1~~~"),
NULL,
thread_one_main,
SCHED_OTHER);
mod_if_register_thread(THREAD_TWO_ID,
INIT_TOP_RIGHT,
3,
const_cast<char *>("test thread2~~~"),
NULL,
thread_two_main,
SCHED_OTHER);
init_module_manager();
}
GG = g++
CPPFLAGES = -std=c++0x -pthread

CPPRCS := $(wildcard *.cpp)
OBJS := $(CPPRCS:%.cpp=%.o)

main: main.o module.o
$(GG) $(CPPFLAGES) -o $@ $^
@echo $@

%.o: %.cpp
$(GG) $(CPPFLAGES) -c $< -o $@
@echo $@

.PHONY: clean
clean:
rm -rf *.o main