Linux应用软件编程---多任务(线程)(线程创建、消亡、回收、属性、与进程的区别、线程间通信、函数指针)
线程
一、什么是线程?
线程是操作系统任务调度的最小单位。是一个轻量级的进程,可实现多任务并发。
二、线程的创建
线程由某个进程创建。
进程创建线程时,会为其分配独立的(8M)栈区空间;线程和所在进程,以及进程中的其他线程,共用进程的堆区、数据区、文本区。
三、线程的调度
宏观并行,微观串行。
四、线程的消亡
1. 线程退出
1)在线程任务函数中使用 return 结束线程;
2)使用 pthread_exit() 函数结束线程。
2. 回收线程资源空间
使用 pthread_join() 函数回收资源空间。
五、进程和线程的区别
1、进程
1)进程是操作系统资源分配的最小单位。
2)资源消耗:进程资源开销大,每次创建都需要有0-4G的虚拟内存空间。
3)效率角度:由操作系统创建,创建时耗时比线程大;跨进程调度比跨线程调度慢。
4)通信方面: 进程间不能直接通信,需要使用进程间通信机制(IPC机制)。
5)安全性角度:进程安全性比线程高,因为各进程空间独立。
2、线程
1)线程是操作系统任务调度的最小单位。
2)资源消耗:资源开销较小,只需要所在进程为其开辟8M的栈区空间。
3)效率角度:由所在进程创建;跨进程调度比跨线程调度慢。
4)通信方面:通信简单,可以使用线程共享的区域进行通信(比如全局变量)。
5)安全性角度:线程没有进程安全性好,一个线程异常可能影响同一进程中的所有线程。
六、线程的相关编程
均需包含头文件:#include<pthread.h>
* 注:1. 线程编译时需确保进程未被关闭,即 要主函数末尾使用 sleep(1) 或 while(1){} 来解决;
2. 编译时,需加上“ -pthread ”,例如: gcc pthread_main.c -pthread 。
1、线程的创建
pthread_create() 创建线程
pthread_self() 获取当前线程的ID号
int pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine) (void *),void *arg); |
功能:创建一个新的线程 失败:非0 /**** void *(*start_routine) (void *)****/ |
2、线程的调度
由操作系统调度。
3、线程的消亡
1)线程退出
(1)在线程任务函数中使用 return 结束线程
(2)使用 pthread_exit() 函数结束线程
void pthread_exit(void *retval) |
功能:用来在某时结束一个线程 参数: retval:向回收的线程传递的参数的地址 为 NULL :表示不传递参数 eg. pthread_exit(NULL) 返回值: 成功:0 失败:非0 |
2)线程回收
使用 pthread_join() 函数回收线程资源空间
int pthread_join(pthread_t thread, void **retval) |
功能:阻塞等待回收线程资源空间 参数: thread:要回收的线程ID号 retval:用来保存线程退出时传递的参数 为 NULL :不接收传递的参数 返回值: 成功:0 失败:-1 |
七、线程的属性
1、分离属性
不需要被其他线程回收的线程称为分离属性的线程,将来被操作系统回收。
2、非分离属性
可以被其他线程回收或结束的线程称为非分离属性线程。
默认线程的属性为:非分离属性
八、线程的回收策略
1、分离属性的线程
不需要回收,自动被操作系统回收。
可采用 pthread_detach() 函数将线程设置成具有分离属性的线程。
int pthread_detach(pthread_t thread); |
功能:将线程设置成具有分离属性的线程 参数: |
2、非分离属性的线程
采用 pthread_join() 函数来阻塞等待回收。
九、线程间通信
在全局变量、全局队列、共享内存区域通信。
1、原因
因为多个线程在访问临界资源时存在资源竞争。
临界资源:多个线程可以同时访问的资源,比如全局变量、共享内存空间等。
2、如何解决资源竞争问题
互斥机制:多个线程访问临界资源时,具有排他性的机制 (一次只允许访问一个线程对该临界资源进行访问)。
即,使用 互斥锁 解决资源竞争问题。
3、实现步骤
1)创建互斥锁:pthread_mutex_t
2)初始化互斥锁:pthread_mutex_init()
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); |
功能:初始化互斥锁 参数: mutex:锁对象的地址 attr:锁的属性 为 NULL :默认属性 返回值: 成功:0 失败:-1 |
eg. pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); |
3)加锁:pthread_mutex_lock()
int pthread_mutex_lock(pthread_mutex_t *mutex); |
4)解锁:pthread_mutex_unlock()
int pthread_mutex_unlock(pthread_mutex_t *mutex); |
5)销毁锁:pthread_mutex_destroy()
int pthread_mutex_destroy(pthread_mutex_t *mutex); |
十、应用示例
1、创建一个线程,并在其中打印一个学生信息
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>typedef struct student
{int id;char name[32];
}str;void *task(void *arg)
{struct student *s = ((struct student *)arg); // ->//struct student s = *((struct student *)arg); // .for(int i = 0; i < 2; i++){printf("tid = %lx\n", pthread_self());printf("%d %s %d", s->id, s->name);}return NULL;
}int main(void)
{pthread_t tid;struct student str[1] = {1, "zhangsan"};int ret = pthread_create(&tid, NULL, task, str);if(ret != 0){printf("pthread_create error!\n");return -1;}sleep(2);/*或while(1){}*/return 0;}
2、使用线程同时执行五个任务模块。
#include <unistd.h>
#include <fcntl.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>typedef void *(*PFUN_t)(void *); //函数指针void *main_ctl(void *arg)
{while (1){printf("主控模块正在工作..pid = %d\n", getpid());sleep(1);}
}void *get_ctr(void *arg)
{ while (1){printf("获取指令模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *put_ctr(void *arg)
{while (1){printf("指令控制模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *get_pic(void *arg)
{ while (1){printf("图像采集模块正在工作...pid = %d\n", getpid());sleep(1);}
}void *put_pic(void *arg)
{while (1){printf("图像发送模块正在工作...pid = %d\n", getpid());sleep(1);}
}int main(int argc, const char *argv[])
{//主进程、主线程pthread_t tid[5] = {0};PFUN_t tasks[5] = {main_ctl, get_ctr, put_ctr, get_pic, put_pic}; //函数指针数组for(int i = 0; i < 5; i++){int ret = pthread_create(&tid[i], NULL, tasks[i], NULL);if (ret !=0){printf("pthread_create error\n");return -1;}}while (1)/***确保进程没有关闭***/{}return 0;
}
3、使用互斥锁完成两个线程任务的有序打印。
#include <stdio.h>
#include <pthread.h>int num_g = 0;
pthread_mutex_t mutex; //全局区 互斥锁创建void *task1(void *arg)
{for(int i = 0; i < 1000; i++){pthread_mutex_lock(&mutex); //加锁num_g = num_g + 1;printf("num_g = %d\n", num_g);pthread_mutex_unlock(&mutex); //解锁}
}void *task2(void *arg)
{for(int i = 0; i < 1000; i++){pthread_mutex_lock(&mutex); //加锁num_g = num_g + 1;printf("num_g = %d\n", num_g);pthread_mutex_unlock(&mutex); //解锁}
}int main(void)
{pthread_t tid[2];//保存线程ID的变量地址pthread_mutex_init(&mutex, NULL);//初始化互斥锁/*****创建线程*****/pthread_create(&tid[0], NULL, task1, NULL);pthread_create(&tid[1], NULL, task2, NULL);/*****线程回收*****/pthread_join(tid[0], NULL);pthread_join(tid[1], NULL);/*****互斥锁的销毁*****/pthread_mutex_destroy(&mutex);return 0;
}
十一、函数指针
1、定义
返回值类型 ( *指针名称)(形参表)
eg. void *(*fun)(void *) |
2、函数指针初始化
返回值类型 ( *指针名称)(形参表) = 函数的地址
eg. void *(*fun)(void *) = main_ctl |
main_ctl 为指向的函数 |
3、函数指针赋值
返回值类型 ( *指针名称)(形参表) = 函数的入口地址
eg. void *(*fun)(void *) = NULL; |
fun = main_ctl |
4、函数指针的使用
函数指针(实参表)
5、函数指针的数组
返回值类型 ( *数组名称[n])(形参表)
eg. void *( *fun[5])(void *); |
【END】