当前位置:   article > 正文

嵌入式笔记Linux ——《线程》_嵌入式线程由什么控制

嵌入式线程由什么控制

线程概述:

每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,需要较大的系统开销。

为了减少系统开销,从进程中演化出了线程。
线程存在于进程中,共享进程的资源。

线程是进程中的独立控制流,由环境(包括寄存器组和程序计数器)和一系列的执行指令组成。

每个进程有一个地址空间、和一个控制线程。

使用多线程设计实现高并发的效果(重点)

线程的基本操作

就像每个进程都有一个进程号一样,每个线程也有一个线程号。

进程号用pid_t数据类型表示,是一个非负整数。线程号则用pthread_t数据类型来表示。

注意:

  1. 进程号在整个系统中是唯一的,但线程号不同,线程号只在它所属的进程环境中有效。
  2. 有的系统在实现pthread_t的时候,用一个结构体来表示,所以在可移植的操作系统实现不能把它做为整数处理。
线程的创建pthread_create()函数

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

参数:

  1. thread:线程标识符地址。
  2. attr:线程属性结构地址。
  3. start_routine:线程函数的入口地址(重点)。
  4. arg:传给新线程执行函数的参数。

返回值:成功返回0,失败返回非0;

注意:

  1. 与fork不同的是pthread_create创建的线程不与父线程在同一点开始运行,而是从指定的函数开始运行,该函数运行完后,该线程也就退出了。
  2. 线程依赖进程存在的,如果创建线程的进程结束了,线程也就结束了。
  3. 线程函数的程序在pthread库中,故链接时要加上参数-lpthread。
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int var = 8;

/* 线程函数:1*/
void *thread_1(void *arg)
{
    while (1)
    {
        printf("This is my new thread1 var++ %d\n");
        var++;
        sleep(1);
    }
    return NULL;
}

/*线程函数:2*/
void *thread_2(void *arg)
{
    while (1)
    {
        printf("This is my new thread2 var = %d\n",var);/* code */
        sleep(1);
    }
    return NULL;
}

int main()
{
    /*创建线程号*/
    pthread_t tid1,tid2;

    /*创建两个线程*/
    pthread_create(&tid1,NULL,thread_1,NULL);
    pthread_create(&tid1,NULL,thread_2,NULL);

    while (1)
    {
        /* code */
    }
    

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
运行结果:


分析:var 是 进程资源,两个线程共享改进程资源,一个线程使var++,另一个线程打印var的值

pthread _ join()函数

#include <pthread.h>
int pthread _ join(pthread_t thread, void **value_ptr);

功能:等待子线程结束,并回收子线程资源(阻塞状态)

参数:

  1. thread:被等待的线程号。
  2. value_ptr:指针的地址,调用此函数后,指针指向一个存储线程完整退出状态的静态区域,可用来存储被等待线程的返回值。

返回值:成功返回0,失败返回非0。

示例代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void *thread(void *arg)
{
	int i;
	static int test = 1213;
	
	for( i = 0; i < 5; i++)
	{
		printf("I am runing \n");
	}

	/*退出函数要求(void *)  退出后带返回值test*/
	pthread_exit( (void *) (&test) );

	return NULL;
}

int main(int argc,char * argv[])
{
	void *vaule_ptr;

	int ret = 0;

	pthread_t tid1;

	/*创建线程*/
	ret = pthread_create(&tid1,NULL,thread,NULL);
	if(ret != 0)
	{
		perror("pthread_create");
	}
	
	/*等待一个子线程退出,退出后回收线程资源*/
	pthread_join(tid1,&vaule_ptr);
	
	/*打印线程结束后所带回来的返回值*/
	printf("vaule_ptr = %d\n",*( (int *)vaule_ptr) );

	sleep(1);

	printf("I am leaving \n");

	while(1);

	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
运行结果:


注意:
创建一个线程后应回收其资源,但使用pthread_join函数会使调用者阻塞,故Linux提供了线程分离函数:pthread_detach。

pthread_detach()函数

#include <pthread.h>
int pthread_detach(pthread_t thread);

功能:使调用线程与当前进程分离,使其成为一个独立的线程,该线程终止时,系统将自动回收它的资源。

参数:thread:线程ID

返回值:成功:返回0,失败返回非0。

示例代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void *thread(void *arg)
{
	int i;
	static int test = 1213;
	
	for( i = 0; i < 5; i++)
	{
		printf("I am runing \n");
		sleep(1);
	}
	
	return NULL;
}

int main(int argc,char * argv[])
{
	int ret = 0;

	pthread_t tid1;

	/*创建线程*/
	ret = pthread_create(&tid1,NULL,thread,NULL);
	if(ret != 0)
	{
		perror("pthread_create");
	}
	
	/*分离 tid1 的线程*/
	pthread_detach(tid1);

	/*等待tid1线程结束,回收资源*/
	pthread_join(tid1,NULL);
	
	/*线程一旦分离,pthread_jojn()将不会等待*/

	/*最一开始打印*/
	printf(" after jion \n ");

	sleep(3);//线程打印

	printf("I am leaving \n");

	return 0;//进程结束 线程也结束
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
运行结果:


在进程中我们可以调用exit函数或_exit函数来结束进程,在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。

  1. 线程从执行函数中返回(自然死亡)
  2. 线程调用pthread_exit退出线程(自杀)
  3. 线程可以被同一进程中的其它线程取消(谋杀)
pthread_exit()函数

#include <pthread.h>
void pthread_exit(void *value_ptr);

功能:
使调用线程退出。

参数:

  1. value_ptr:指向线程退出状态的静态区域,在别的线程中可用pthread_join访问。
  2. 一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。
    示例看:pthread _ join()函数的示例
pthread_cancel()函数

int pthread_cancel(pthread_t thread);

功能:取消线程:是指取消一个正在执行线程的操作。

参数:thread:目标线程ID。

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

注意:

  1. pthread_cancel函数的实质是发信号给目标线程thread,使目标线程退出。
  2. 此函数只是发送终止信号给目标线程,不会等待取消目标线程执行完才返回。
  3. 然而发送成功并不意味着目标线程一定就会终止,线程被取消时,线程的取消属性会决定线程能否被取消以及何时被取消。

Linux系统下,线程默认可以被取消。编程时可以通过pthread_setcancelstate函数设置线程是否可以被取消。

pthread_setcancelstate(int state,int *old_state);

  1. state:PTHREAD_CANCEL_DISABLE:不可以被取消、
    PTHREAD_CANCEL_ENABLE:可以被取消。
  2. old_state
    保存调用线程原来的可取消状态的内存地址。
示例代码:
#include <unistd.h>
#include <pthread.h>

void *thread_cancel(void *arg)
{
    //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//禁能接收取消信号
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//使能接收取消信号
//ENABLE使能:等线程接收到取消信号就退出
//DIISABLE禁能:不接收取消信号
    while(1)
    {
        printf("This is my new thread_cancel\n");
        sleep(1);
    }
    
    return NULL;
}

int main(int argc ,char *argv[])
{
    int ret = 0;

    pthread_t tid1;

    ret = pthread_create(&tid1,NULL,thread_cancel,NULL);
    if(ret != 0)
    {
        perror("pthread_create");
    }

    sleep(3);

    pthread_cancel(tid1);

    pthread_join(tid1,NULL);

    return 0;
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
运行结果:

和进程的退出清理一样,线程也可以注册它退出时要调用的函数,这样的函数称为线程清理处理程序(thread cleanup handler)。

注意:

  1. 线程可以建立多个清理处理程序。
  2. 处理程序在栈中,故它们的执行顺序与它们注册时的顺序相反。

pthread_cleanup_push()函数

#include <pthread.h>
void pthread_cleanup_push(void (routine)(void), void *arg);

功能: 将清除函数压栈。即注册清理函数。

参数:

  1. routine:线程清理函数的指针。
  2. arg:传给线程清理函数的参数。

pthread_cleanup_pop()函数

#include <pthread.h>
void pthread_cleanup_pop(int execute);

功能:将清除函数弹栈,即删除清理函数。

参数:

  1. execute:线程清理函数执行标志位。
    非0,弹出清理函数,执行清理函数。
    0,弹出清理函数,不执行清理函数

当线程执行以下动作时会调用清理函数:

  1. 调用pthread_exit退出线程。
  2. 响应其它线程的取消请求。
  3. 用非零execute调用pthread_cleanup_pop。
  4. 无论哪种情况pthread_cleanup_pop都将删除上一次pthread_cleanup_push调用注册的清理处理函数。

注意:
pthread_cleanup_pop()函数、pthread_cleanup_push()必须配对使用。

示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

void cleanup(void *arg)
{
    printf("clean ip ptr = %s\n",(char *)arg);
    /*释放申请的堆区内存*/
    free( (char *)arg);
}

void *thread(void *arg)
{
    char *ptr = NULL;

    printf("This is new thread\n");

    /* 在堆区申请100个字节的空间 */
    ptr = (char *)malloc(100);

    /*把cleanup函数入栈*/
    pthread_cleanup_push(cleanup,(void *)(ptr));

    /* 清空申请空间 */
    bzero(ptr,100);
    /* 填充申请空间 */
    strcpy(ptr,"memory form malloc");

    printf("before exit\n");
    /* 退出线程 */
    pthread_exit(NULL);

    sleep(3);

    printf("before pop\n");                                                                                                                                                                                                                                                                    

    pthread_cleanup_pop(0);
    //pthread_cleanup_pop(1);

    return NULL;
}

int main(int argc,char *argv[])
{
    int ret = 0;
    pthread_t tid1;

    /* 创建线程 */
    ret = pthread_create(&tid1,NULL,thread,NULL);
    if(ret != 0)
    {
        perror("pthread_create");
    }

    /* 等待线程的结束 */
    pthread_join(tid1,NULL);

    printf("Process is dying\n");

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
运行结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/181952?site
推荐阅读
相关标签
  

闽ICP备14008679号