嵌入式技术笔记(十六):消息队列和线程

本文详细介绍了Linux系统中的消息队列,包括POSIX和System V消息队列的区别,如接口复杂度、可移植性和消息读取机制。接着,解释了线程的概念、作用、组成、特点以及创建线程的步骤。通过示例代码展示了线程的创建、参数传递、线程等待、线程退出和线程状态。最后,对比了进程和线程的区别,并讨论了线程的状态转换和线程管理的相关函数。
摘要由CSDN通过智能技术生成

1. 消息队列
传送有格式的消息流
多进程网状交叉通信,实现大规模数据通信
使用内核中的链表(实现机制)

  • posix的消息队列和系统V消息队列的区别
    1、一般来说posix的接口要比systemV的简单,但是systemV的可已移植性更好几乎所有的unix系统都支持。
    2、对posix消息队列的读总是返回最高优先级的最早消息,对systemV消息队列的读则可以返回
    任意指定优先级的消息。
    3、当往一个空队列放置一个消息时,posix消息队列允许产生一个信号或者启动一个线程,systemV消息队列则不提供类似的机制。

消息队列是随内核持续的,所以会产生的现象是:消息队列随内核开始而开始,随内核结束而结束。

2. 线程

  • 什么是线程
    线程是进程的一个实体,它是程序运行的最小单位。

  • 为什么要学习线程(线程的作用)
    1、线程是进程的一个实体,它是程序运行的最小单位,它比进程要消耗更少的资源
    2、能共享地址空间(进程的地址空间)(堆栈:程序栈)

  • 线程由哪些组成
    1、指令指针(指向当前执行的命令)
    2、一个栈(函数栈)
    3、寄存器的集合(状态寄存器:一部分正在运行中的处理器的状态)
    4、一个私有的数据区

  • 线程的特点
    1、线程切换的开销很低(因为线程切换的实质是函数切换)
    2、线程的通信机制简单(由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,即共享进程的地址空间)

  • 多线程切换的实质:函数切换

    线程并不是操作系统中内核所提供的而是由线程库来提供libpthread.a/.so
    线程的调用离不开ios的支持

3. 创建线程

  • int pthread_create()
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    pthread_t *thread:指向线程的指针;
    const pthread_attr_t *attr:创建线程时的属性,一般是NULL,即无属性创建;
    void *(*start_routine) (void *):线程运行的实体函数指针;
    void *arg:线程的参数;
    注意:线程是进程的一个实体,一旦主进程运行结束,线程就会被回收。
  • 代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

void *func(void *arg)  //次线程,子线程
{
    while(1)
    {
        printf("This is a thread\n");
	    sleep(2);
    }
    
}

int main()  
{
    pthread_t id;
    int ret = pthread_create(&id, NULL, func, NULL);
    if(0 != ret)
    {
	perror("pthread_create error!");
	exit(0);
    }
    while(1)
    {
	printf("main thread!\n");
	sleep(2);
    }
    pause();  //pause()表示挂起子线程;或者用while(1);
    return 0;
}

运行结果如下:可以看出主线程和子线程是竞争关系
函数指针无参数

  • 次线程函数的参数是int型时,代码如下
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

void *func(void *arg)  
{
    int *num = (int *)arg;
    printf("This is a subthread:%d\n", *num);
    sleep(2);
    
}

int main()  
{
    int i = 5;
    pthread_t id;
    int ret = pthread_create(&id, NULL, func, (void *) &i);
    if(0 != ret)
    {
	perror("pthread_create error!");
	exit(0);
    }
    while(1)
    {
	printf("main thread!\n");
	sleep(2);
    }
    pause();  //while(1);
    return 0;
}

运行结果如下图:因为没有while(1)循环,子线程只输出了一次,但是主线程输出的时候,子线程并没有结束,只是处于挂起状态,仍然消耗资源。当Ctrl+c结束进程时,子线程的资源才会被回收。
函数指针有int型参数

  • 次线程函数的参数为结构体时,代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

struct demo
{
    int id;
    char name[1024];
};

void *func(void *arg)  
{
    struct demo *ptr = (struct demo *)arg;
    printf("subthread id:%d, name:%s\n", ptr->id, ptr->name);
    sleep(2);
    
}

int main()  
{
    struct demo demo1 = {2021, "zhang"};
    pthread_t id;
    int ret = pthread_create(&id, NULL, func, (void *)&demo1);
    if(0 != ret)
    {
	perror("pthread_create error!");
	exit(0);
    }
    while(1)
    {
	printf("main thread!\n");
	sleep(2);
    }
    pause();  //while(1);
    return 0;
}

运行结果如下:
子线程函数的参数为结构体

4. 线程等待
pthread_join()
int pthread_join(pthread_t thread, void **retval);
pthread_t thread:需要等待的线程
void **retval:线程退出的状态
这是一个线程阻塞函数,调用它的进程将一直等待到线程执行完毕为止,而且当函数返回时,线程的资源将被收回。

线程等待的目的:
1、保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个进程退出
2、回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
3、获得新线程退出时的结果是否正确退出返回值

作业:创建两个次线程,两个次线程分别向同一个文件写“hello”“world\n”,和"hhhhh"“wwwww\n”

5. 线程退出

  • 被动退出int pthread_cancel(pthread_thread);
    (1)功能
    当次线程是死循环时,可以调动这个函数主动取消该线程。
    (2)返回值
    成功返回0,失败返回非零错误号。发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
    (3)参数
    thread:要取消线程的TID。
    当不发生系统调用时,线程不会结束。

代码如下:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX_SIZE 1024


void *thread1(void *arg)  //ci xian cheng
{
    while(1)
    {
	printf("hi\n");
	sleep(1);
    }
}


int main()  //main han shu xia de shi zi xian cheng
{
    pthread_t id1;
       
        if(pthread_create(&id1, NULL, thread1, NULL) != 0)
    {
	perror("pthread_create error!");
	exit(0);
    }
    sleep(3);

    pthread_cancel(id1);
    pause();
    return 0;
}

如下图,当次线程发生printf()这一系统调用时,可以用pthread_cancel()函数取消;但是将printf()注释掉之后(即没有发生系统调用时),线程不会结束。(在次线程里定义int i,并用i++;此时,i++不算是系统调用)
pthread_cancel函数

  • 主动退出
    void pthread_exit(void *retval);
    1、功能:线程调用这个函数时,可以主动退出(终止)。
    它和exit()很像,exit()是终止整个程序,而pthread_exit是终止次线程。
    如果在次线程里面调用错误,调用的是exit,整个线程就终止了。
    2、返回值
    成功返回值为0,失败返回非0
    3、参数
    retval:线程结束的返回值
    如果返回值很多时,就会封装成一个结构体,返回结构体变量的地址即可。
    正确代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX_SIZE 1024

int num = 5;

void *thread1(void *arg)  //ci xian cheng
{
    while(1)
    {
	printf("hi\n");
	sleep(1);
	//pthread_exit(&num);
	pthread_exit(NULL);
    }
}


int main()  //main han shu xia de shi zi xian cheng
{
    //int *num; 
    pthread_t id1;
       
        if(pthread_create(&id1, NULL, thread1, NULL) != 0)
    {
	perror("pthread_create error!");
	exit(0);
    }
    sleep(3);

    pthread_cancel(id1);
    pthread_join(id1, (void *)&num);

    printf("thread is exit! %d\n", *((int *)num));
    pause();
    return 0;
}

运行结果如下:
pthread_exit()结果

  • return ()退出线程(但不会弹栈,退出后数据不会弹出栈
    举例:return (void *)0;

  • 注册线程退出处理函数
    void pthread_cleanup_push(void (*routine)(void *), void *arg);
    void pthread_cleanup_pop(int execute);
    void(*rtn)(void *): 线程清理函数
    这两个函数是成对出现。
    弹栈线程退出处理函数的几种条件
    pthread_cleanup_pop(!0):主动弹栈;(pthread_cleanup_pop(0)表示不会弹栈)
    如果线程是被别人调用pthread_cancel取消的,也会弹栈;
    如果调用pthread_exit函数也会主动弹栈;
    ==注意:==return退出的话是不会自动弹栈的,想要自动弹栈用pthread_cleanup_pop(!0)

6. 关于进程和线程的不同点
1、进程关心进程间的通信;线程关心共享的数据资源(线程的通信靠全局变量
2、每个进程都是单独的地址空间;多个线程可以共享一个进程的地址空间,线程是进程的一个实体。

7. 线程的状态
1、可结合态:这种状态下的线程是能被其他进程回收资源或被杀死的
2、可分离态:不能被其他进程回收资源或被杀死的,它存储的资源在它终止时由系统自动回收
默认情况下,线程是可结合态

  • 线程分离函数pthread_detach()
    1、功能:如果次线程不希望别人调用pthread_join()函数来回收,而是希望自己在运行结束时,自动回收资源调用pthread_detach();
    将pthread_detach()中的线程变成分离态
    2、返回值:成功返回1;错误返回一个非零值
    3、参数:thread:要分离的那个次线程的TID

8. 进程和线程的区别
1、进程——资源分配的最小单位,线程——资源调度的最小单位
2、每个进程有独立的地址空间,多个线程共享进程的地址空间,线程间的切换比进程间的切换开销要小。
3、线程的调度必须通过频繁地加锁来保持线程的同步,影响线程的并发性能。
4、进程比线程更加健康,多进程之间相互独立,一个进程的异常对其他进程不影响,而一个线程的崩溃可能影响其他线程或者整个程序。
5、通信方式不同:线程间的通信方便(小数据量),同一进程下共享全局变量还有静态变量等数据;而进程间的通信需要以通信的方式(IPC)进行。不过如何处理好同步和互斥是编写多线程程序的难点。
6、多线程代码结构比多进程代码结构简单易读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值