线程
思考:一个项目中可能很多的功能模块需要同时运行,并且每个模块中都有独立的循环,但是循环内部没有编写退出条件,就会出现死循环的现象,无法让多个模块同时运行,请问如何解决该问题?
回答:可以在一个进程中创建多条线程来解决,进程指的是进行中的程序,也就是说一个程序得到运行就会变为进程。
线程的创建:



注意:线程使用的是进程的资源,所以进程退出之后线程也会退出,而进程结束的条件之一就是程序的main函数退出,所以也就是当程序中的main函数调用return语句则进程会被终止,所以进程的其他的线程也会一起退出。
点击查看代码
/********************************************
* 主线程:打开触摸屏
* 子线程:获取触摸屏坐标
************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <linux/input.h>
volatile int x,y;
//获取触摸屏坐标
void get_value(int tp_fd,int *tp_x,int *tp_y){
int cnt = 0;
struct input_event tp_event;
while(1){
read(tp_fd,&tp_event,sizeof(tp_event));
if(tp_event.type == EV_ABS && tp_event.code == ABS_X){
*tp_x = tp_event.code;
cnt++;
}
if(tp_event.type == EV_ABS && tp_event.code == ABS_X){
*tp_y = tp_event.code;
cnt++;
if(cnt >= 2){
break;
}
}
}
}
//子线程
void *tp_task(void *arg){
int tp_fd = *(int*)arg;
int tp_x,tp_y;
while(1){
get_value(tp_fd,&tp_x,&tp_y);
x = tp_x;
y = tp_y;
printf("x = %d,y = %d\n",x,y);
}
}
//主线程
int main(int argc,char const *argv[]){
//1.打开触摸屏
int tp_fd = open("/dev/input/event0",O_RDWR);
if(-1 == tp_fd){
fprintf(stderr,"open tp_fd error,errorno:%d,%s\n",errno,strerror(errno));
exit(1);
}
//2.创建子线程
pthread_t tp_pthread;
pthread_create(&tp_pthread,NULL,tp_task,(void *)&tp_fd);
return 0;
}
线程的接合:
与进程类似,线程退出之后不会立即释放其所占有的系统资源,而会成为一个僵尸线程。为了防止线程退出之后还占用进程资源,所以Linux系统中提供了一个名称叫做pthread_join()的函数接口,其他线程可使用 pthread_join() 来释放僵尸线程的资源,并可获得其退出时返回的退出值,该函数被称为线程的接合函数。

练习:编写一个程序,主线程中创建一个子线程,容纳后让主线程先退出并返回一个值,子线程接合主线程后输出主线程的退出值,然后子线程退出。提示:获取线程自身的ID可以调用pthread_self()函数。
点击查看代码
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
pthread_t main_thread; //存储主线程ID,注意类型是pthread_t
//线程的任务
void * task(void *arg)
{
//用于存储主线程的终止状态
int *exit_status;
while(1)
{
//子线程等待主线程终止并输出主线程的终止状态,如果等待接合的线程未终止,则该函数会阻塞
pthread_join(main_thread,(void **)&exit_status);
printf("main thread exit status code = %d\n",*exit_status);
pthread_exit(NULL);
}
}
//主线程
int main(int argc, char const *argv[])
{
//1.创建子线程,线程一旦创建成功,则马上开始工作
pthread_t thread;
pthread_create(&thread,NULL,task,NULL);
//2.获取自身的线程ID
main_thread = pthread_self();
//3.主线程终止
int exit_status = 100;
sleep(5);
pthread_exit((void *)&exit_status); //主线程可以优先于子线程结束
return 0;
}
线程的属性:
思考:如果一个线程在执行完任务之后结束,但是使用的资源没有释放,此时线程变为僵尸线程,但是如果没有其他线程调用pthread_join来对该线程进行接合,此时僵尸线程的资源应该如何才可以得到释放?
回答:在程序中创建的线程有很多属性,用户可以调用线程库中的相关接口来获取线程的属性以及设置线程的属性。


在众多的线程属性中,有一个可以让线程在终止之后自动释放资源的属性,那就是线程的分离属性。
当线程终止时如果不需要把退出值传递给其他线程,则可以把线程设置为分离属性,设置为分离属性下的线程在退出之后,会自动释放其占用的系统资源。
Linux系统提供了一个名称叫做pthread_attr_setdetachstate()的函数,用户利用该函数可以在设置线程的属性为分离属性,注意:设置为分离属性的线程是无法被接合的。
练习:设计一个程序,要求在程序中创建两条子线程,主线程创建一个线程的属性对象,并对线程的属性对象进行初始化之后,要求创建的子线程的属性设置为分离属性,子线程A输出”hello”,子线程B输出”world”,主线程创建子线程之后就终止。
点击查看代码
/******************************************************************************************
*
* 线程的属性设置是有步骤、有顺序的:定义对象 + 初始化对象 + 修改对象 + 使用对象
*
* 1.创建一个线程属性对象 pthread_attr_t
* 2.首先需要初始化线程属性对象 pthread_attr_init()
* 3.可以调用其他的线程属性设置函数修改属性对象中的属性 pthread_attr_set....()
* 4.线程属性对象中的属性如果都已经设置完成,则可以调用pthread_create()创建线程
*
* 5.如果新线程是使用线程属性对象创建的,并且已经创建成功,则新线程和该对象无关
* 6.如果线程属性对象没有长期的使用需求,则可以选择调用pthread_attr_destroy()销毁
* 7.如果一个线程属性对象已经被销毁,则可以选择重新使用pthread_attr_init()初始化
*
******************************************************************************************/
#include <stdio.h>
#include <pthread.h>
void *task(void *arg)
{
while(1)
{
printf("I am thread,my TID = %d\n",pthread_self());
pthread_exit(NULL);
}
}
int main(int argc, char const *argv[])
{
//1.定义线程属性对象
pthread_attr_t thread_addr;
//2.初始化线程属性对象
pthread_attr_init(&thread_addr);
//3.修改线程属性对象中的分离属性,设置为分离,也就是线程终止时不需要调用pthread_join
pthread_attr_setdetachstate(&thread_addr,PTHREAD_CREATE_DETACHED);
//4.利用线程属性对象来创建新线程
pthread_t thread;
pthread_create(&thread,&thread_addr,task,NULL);
//5.主线程的任务
while(1)
{
}
return 0;
}
思考:如果用户在创建线程的时候忘记设置线程属性为分离状态,但是又打算让线程在完成任务之后自动释放资源,并且又不想重新创建一条新线程,此时应该如何处理?
回答:可以在线程的任务函数中强制把自身的属性设置为分离属性,Linux系统中提供了一个名称叫做pthread_detach()的函数,可以强制分离。

思考:该函数需要传递一个线程的标识符,如果线程打算把自身强制分离,就需要把该线程的ID传递进来,请问如何获取线程的ID?
回答:Linux系统提供了一个名称叫做pthread_self()的函数接口,线程的TID仅限于进程内部的线程间有效。当需要要对某条线程执行诸如发送信号、取消、阻塞接合等操作时,都需要用到线程的ID。

点击查看代码
#include <stdio.h>
#include <pthread.h>
//可以选择在线程的任务重调用pthread_detach来强制设置该线程的属性为分离属性
void *task(void *arg)
{
//可以设置线程的属性为分离属性,此时线程终止时就不需要其他线程调用pthread_join函数
pthread_detach(pthread_self());
while(1)
{
printf("I am thread,my TID = %d\n",pthread_self());
pthread_exit(NULL); //当线程终止时,线程的资源会自动的释放
}
}
int main(int argc, char const *argv[])
{
//1.创建新线程,线程默认是可结合的,所以当线程终止时,需要其他线程调用pthread_join函数
pthread_t thread;
pthread_create(&thread,NULL,task,NULL);
//5.主线程的任务
while(1)
{
}
return 0;
}
浙公网安备 33010602011771号