首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

linux主线程子线程串口

基础概念

在Linux系统中,主线程(main thread)是程序启动时创建的第一个线程,负责执行程序的主要逻辑。子线程(sub-thread)是由主线程创建的额外线程,用于执行并发任务。串口(Serial Port)是一种用于数据传输的通信接口,通常用于连接外部设备,如传感器、打印机等。

相关优势

  1. 并发处理:通过子线程可以同时处理多个任务,提高程序的执行效率。
  2. 资源利用:子线程可以充分利用多核CPU的优势,提高系统的整体性能。
  3. 模块化设计:将不同的功能模块放在不同的线程中,便于代码的维护和扩展。

类型

  1. 守护线程(Daemon Thread):在主线程结束后自动结束的线程,通常用于后台任务。
  2. 用户线程(User Thread):需要显式管理生命周期的线程,通常用于执行主要任务。

应用场景

  1. 数据采集:通过串口连接传感器,子线程负责实时采集数据并处理。
  2. 设备控制:通过串口发送控制命令,子线程负责接收响应并处理。
  3. 日志记录:主线程执行主要逻辑,子线程负责记录日志。

遇到的问题及解决方法

问题1:串口数据读取不完整

原因:可能是由于主线程阻塞或子线程处理速度不够快导致的。

解决方法

代码语言:txt
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

#define SERIAL_PORT "/dev/ttyUSB0"
#define BUFFER_SIZE 1024

void *read_serial(void *arg) {
    int fd = *(int *)arg;
    char buffer[BUFFER_SIZE];
    while (1) {
        int n = read(fd, buffer, BUFFER_SIZE);
        if (n > 0) {
            // 处理读取的数据
            printf("Received data: %.*s\n", n, buffer);
        }
    }
    return NULL;
}

int main() {
    int fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("Failed to open serial port");
        exit(EXIT_FAILURE);
    }

    struct termios options;
    tcgetattr(fd, &options);
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    tcsetattr(fd, TCSANOW, &options);

    pthread_t thread_id;
    pthread_create(&thread_id, NULL, read_serial, &fd);
    pthread_detach(thread_id);

    // 主线程执行其他任务
    while (1) {
        sleep(1);
    }

    close(fd);
    return 0;
}

参考链接Serial Port Programming in Linux

问题2:子线程与主线程同步问题

原因:可能是由于子线程和主线程之间的数据共享和同步机制不当导致的。

解决方法

代码语言:txt
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

#define SERIAL_PORT "/dev/ttyUSB0"
#define BUFFER_SIZE 1024

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
char shared_buffer[BUFFER_SIZE];

void *read_serial(void *arg) {
    int fd = *(int *)arg;
    char buffer[BUFFER_SIZE];
    while (1) {
        int n = read(fd, buffer, BUFFER_SIZE);
        if (n > 0) {
            pthread_mutex_lock(&mutex);
            strncpy(shared_buffer, buffer, n);
            pthread_mutex_unlock(&mutex);
        }
    }
    return NULL;
}

int main() {
    int fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("Failed to open serial port");
        exit(EXIT_FAILURE);
    }

    struct termios options;
    tcgetattr(fd, &options);
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    tcsetattr(fd, TCSANOW, &options);

    pthread_t thread_id;
    pthread_create(&thread_id, NULL, read_serial, &fd);
    pthread_detach(thread_id);

    while (1) {
        pthread_mutex_lock(&mutex);
        if (strlen(shared_buffer) > 0) {
            printf("Main thread received data: %s\n", shared_buffer);
            memset(shared_buffer, 0, BUFFER_SIZE);
        }
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }

    close(fd);
    return 0;
}

参考链接POSIX Threads Programming

总结

通过上述示例代码和解释,可以解决Linux主线程和子线程在串口通信中遇到的常见问题。关键在于合理设计线程间的同步机制,确保数据的完整性和一致性。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

【EventBus】事件通信框架 ( 发送事件 | 判断发布线程是否是主线程 | 子线程切换主线程 | 主线程切换子线程 )

MAIN , 则需要判定发布线程是否是主线程 ; 如果发布线程是主线程 , 则直接执行订阅方法 ; 如果发布线程不是主线程 , 则需要在主线程中执行订阅方法 ; 假如订阅方法的线程模式属性是 BACKGROUND...线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 ) 博客的部分操作 ; 一、根据不同的线程模式进行不同的线程切换操作 ---- 首先 , 获取当前线程是否是主线程...: 参考 【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 ) 一、判定当前线程是否是主线程 博客章节 ;...】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 ) 二、子线程中执行主线程方法 博客章节 ; case MAIN...分支进行合并处理 ; 参考 【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 ) 三、主线程中执行子线程方法

62710
  • 【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 )

    文章目录 一、判定当前线程是否是主线程 二、子线程中执行主线程方法 三、主线程中执行子线程方法 一、判定当前线程是否是主线程 ---- 在 Android 中 , 如果要判定当前线程是否是主线程 , 可以使用如下方法进行判定...Looper 对象 ; Looper.myLooper() 方法获取的是当前 Looper 线程的 Looper 对象 , 如果当前线程是主线程 , 那么这两个 Looper 对象是相同的 ;...// 判断当前线程是否是主线程 // 获取 mainLooper 与 myLooper 进行比较 , 如果一致 , 说明该线程是主线程 boolean isMainThread...isMainThread = true; } 二、子线程中执行主线程方法 ---- 获取主线程的 Looper , 通过 Looper 创建对应的 Handler , 然后通过该 Handler...中 ; // 将订阅方法放到主线程执行 // 获取主线程 Looper , 并通过 Looper 创建 Handler

    1.2K10

    (一)主线程与工作线程的分工

    这里我们将线程A称为主线程,B1、B2、B3、B4等称为工作线程。工作线程的代码框架一般如下: while (!...我们采取如下方法来解决该问题,以linux为例,不管epoll_fd上有没有文件描述符fd,我们都给它绑定一个默认的fd,这个fd被称为唤醒fd。...这个唤醒fd,在linux平台上可以通过以下几种方法实现: 1. 管道pipe,创建一个管道,将管道绑定到epoll_fd上。需要时,向管道一端写入一个字节,工作线程立即被唤醒。...2. linux 2.6新增的eventfd: int eventfd(unsigned int initval, int flags); 步骤也是一样,将生成的eventfd绑定到epoll_fd上...即linux特有的socketpair,socketpair是一对相互连接的socket,相当于服务器端和客户端的两个端点,每一端都可以读写数据。

    2K90

    Android 主线程中 Looper.loop() 为什么不会卡死主线程?

    1, 这里涉及到Linux pipe/epoll机制 ,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里。...此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。...,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程. public static void main(String[] args...//创建Looper和MessageQueue对象,用于处理主线程的消息 Looper.prepareMainLooper(); //创建ActivityThread对象...这里同样是涉及到Linux pipe/epoll机制,可参考不卡死主线程的原因。对Linux pipe/epoll机制有兴趣可google相关的介绍 Looper.loop() 会发生ANR 吗?

    1.5K10

    libuv线程池和主线程通信原理

    执行QUEUE_INSERT_TAIL给libuv的async_handles队列追加一个handle(写端,线程池的线程完成任务后会使用写端写入数据,通知主线程) 下面我们看一下1,2两点的实现。...UV_ENOSYS) { // 不支持eventfd则使用匿名管道 err = uv__make_pipe(pipefd, UV__F_NONBLOCK); #if defined(__linux...loop->wq_async是写端相关的handle,线程池会设置这个handle的pending为1表示有任务已经完成,然后再往管道写端写入标记,主线程在epoll_wait的时候返回这个fd,并指向对应的回调...就这样完成了线程池和主线程的通信。下面我们看看使用的例子。 这里以文件操作为例子,因为nodejs中文件读写是以线程池实现的。这里直接从uv_fs_open开始(因为js层到c++层主要是一些封装。...最后执行uv_async_send(&w->loop->wq_async)通知主线程。w->loop->wq_async就是我们前面说到的写端对应的handle。

    1.5K10

    关于主线程中自动建立的Looper的思考:主线程中Looper中的轮询死循环为何没有阻塞主线程

    Android中UI线程会自动给我们建立一个looper,但是looper中的loop方法是个死循环.为什么我们在UI线程中写的代码为何都能顺利执行?为什么没有引起ANR呢?...been prepared."); } sMainLooper = myLooper(); } } 从源码的注释中,我们可以看出主线程中..." what=" + msg.what); } msg.recycle(); } } 可以看出loop的确是个死循环.那么在主线程有个死循环...,这仔细想一想不对劲,这样按常理主线程早就被阻塞报ANR异常啊.但是我们平时开发的时候似乎根本就不受这个死循环的影响....static final boolean DEBUG_MESSAGES = false; .......省略 } 从ActivityThread类的注释上可以知道这个类管理着我们平常所说的主线程

    1.3K40

    main函数是主线程吗

    1、线程的概念: 线程是程序最基本的运行单位,而进程不能运行,所以能运行的,是进程中的线程。 2、线程是如何创建起来的: 进程仅仅是一个容器,包含了线程运行中所需要的数据结构等信息。...一个进程创建时,操作系统会创建一个线程,这就是主线程,而其他的从线程,却要主线程的代码来创建,也就是由程序员来创建。...当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程...每个进程至少都有一个主线程,在Winform中,应该就是创建GUI的线程。  主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行比如执行各种关闭动作。...这个进程中,可以包含多个线程,也可以只包含一个线程。当用c写一段程序的话,就是在操作系统中起一个进程它包含一个线程。

    2.1K40

    主线程异常会导致 JVM 退出?

    ,在这个线程里搞一个 while true 不断打印, 然后在主线程中制造一个空指针异常,不捕获,然后看是否会一直打印 test 结果是会不断打印 test,说明主线程崩溃,JVM 并没有崩溃,这是怎么回事...其实在 Java 中并没有所谓主线程的概念,只是我们习惯把启动的线程作为主线程而已,所有线程其实都是平等的,不管什么线程崩溃都不会影响到其它线程的执行,注意我们这里说的线程崩溃是指由于未 catch 住...在 Linux 中进程分配资源后,线程通过共享资源的方式来被调度得以提升线程的执行效率 由此可见,在 Linux 中所有的进程/线程都是用的 task_struct,它们之间其实是平等的,那怎么表示这些线程属于同一个进程的概念呢...task_struct 中引入了线程组的概念,如果线程都是由同一个进程(即我们说的主线程)产生的, 那么它们的 tgid(线程组id) 是一样的,如果是主线程,则 pid = tgid,如果是主线程创建的线程...,则这些线程的 tgid 会与主线程的 tgid 一致, 那么在 LInux 中进程,进程内的线程之间是如何通信或者管理的呢,其实 NPTL 是一种实现了 POSIX Thread 的标准 ,所以我们只需要看

    1.4K20

    Android中检测当前是否为主线程

    如果在Android中判断某个线程是否是主线程?对于这个问题,你可能说根据线程的名字,当然这个可以解决问题,但是这样是最可靠的么?万一某天Google一下子将线程的名字改称其他神马东西呢。...Looper{40d35ef8} I/TestInMainThread(32028): testInMainThread inMainThread=true 实验二 现在我们继续在一个没有消息循环的非主线程...通过这个方法,主线程的looper被创建,并且将对象引用传递给sMainLooper。所以保证了主线程myLooper()获取到的引用和getMainLooper()获取到的都是同一个引用。...对于没有消息循环的非主线程,默认的当前线程的looper是null,因为你从来没有手动地调用prepare(),所以它和主线程的looper不一样。...对于绑定了消息循环的非主线程,当调用Looper.prepare方法时,主线程的Looper已经由Android运行环境创建,当调用prepare方法后,绑定到这个非主线程的looper被创建,当然,这不可能和主线程的

    90930

    Android子线程更新UI主线程方法之Handler

    Handler的主要作用:主要用于异步消息的处理 Handler的运行过程: 当(子线程)发出一个消息之后,首先进入一个(主线程的)消息队列,发送消息的函数即刻返回,而在主线程中的Handler逐个的在消息队列中将消息取出...在大白话一点的介绍它的运行过程: 启动应用时Android开启一个主线程 (也就是UI线程) , 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中...,如果你放在主线程中的话,界面会出现假死现象(这也就是你在主线程中直接访问网络时会提示你异常的原因, 如下所述)。...这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,更新UI只能在主线程中更新.。...(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。

    2.2K90

    java 主线程等待子线程执行完后再执行

    这里记录一下下面这种情况:主线程需要等待多个子线程执行完后再执行。...-5 子线程正在执行:Thread-6 子线程正在执行:Thread-7 子线程正在执行:Thread-8 主线程正在执行后:main 子线程正在执行:Thread-9 可以看到,子线程还没执行完时,主线程进来了...-5 子线程正在执行:Thread-6 子线程正在执行:Thread-7 子线程正在执行:Thread-8 子线程正在执行:Thread-9 主线程正在执行后:main 或者用java8之前的方式写:...System.out.println("主线程正在执行后:"+Thread.currentThread().getName()); } } 结果为: 主线程正在执行前:main...,当前线程为:Thread-9 子线程正在执行任务,当前线程为:Thread-8 主线程正在执行后:main 附: 开启一个线程的其他写法: /**jdk7匿名内部类的写法*/ public

    4.6K20

    主线程和子线程下的事务不回滚【spring】

    -- 线程池维护线程的最少数量 --> 主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 --> 线程抛异常了主线程能回滚吗?...答案是不能,因为主线程拿不到子线程抛的异常信息,spring事务管理的是当前线程下的,并且事务的隔离级别默认是 PROPAGATION_REQUIRED--支持当前事务,假设当前没有事务。...因为这样毫无意义,如果把同一个连接传到子线程,那就是SQL操作会串行执行,那何必还多线程呢,很显然,在另外一个线程下自然会创建一个新的事物,而不是进行事务传播,所以不能够回滚业务 这个时候,我想到了这个类

    2.7K50
    领券