You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程向同一串口写入数据,该操作是否具备线程安全性?

多线程写入串行端口的线程安全性问题

问题核心

你的代码中多个写线程直接调用write操作同一个串口文件描述符,这种做法不具备线程安全性。

原因分析

在Linux系统中,write系统调用仅对普通文件在写入大小不超过PIPE_BUF时保证原子性,但串口属于字符设备,其驱动的write实现不保证多线程并发写入的原子性:

  • 多个线程同时调用write时,发送的数据会被交错拼接,比如线程1的"writer1"和线程2的"writer2"可能被混拼成"wriwter2ter1"这类错误内容。
  • 你的写线程通过循环调用write来发送完整字符串,但这个循环过程没有同步机制,不同线程的write调用会互相打断,导致数据乱序。

解决方案

要实现线程安全的串口写入,必须给写入操作添加同步锁,确保同一时间只有一个线程执行写入操作:

修改后的代码示例

#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>

int set_interface_attribs (int fd, int speed, int parity) {
    struct termios tty;
    if (tcgetattr (fd, &tty) != 0)
    {
        return -1;
    }

    cfsetospeed (&tty, speed);
    cfsetispeed (&tty, speed);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
    // disable IGNBRK for mismatched speed tests; otherwise receive break
    // as \000 chars
    tty.c_iflag &= ~IGNBRK;         // disable break processing
    tty.c_lflag = 0;                // no signaling chars, no echo,
                                    // no canonical processing
    tty.c_oflag = 0;                // no remapping, no delays
    tty.c_cc[VMIN]  = 1;            // read blocks
    tty.c_cc[VTIME] = 0;            // 0 seconds read timeout

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

    tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                    // enable reading
    tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
    tty.c_cflag |= parity;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr (fd, TCSANOW, &tty) != 0)
    {
        return -1;
    }
    return 0;
}

const char *portname = "/dev/ttyUSB1";
int fd = -1;
pthread_mutex_t write_mutex; // 写入同步锁

void* writer(void* data) {
    const char* msg = (char*)data;
    while (true) {
        int cnt = 0, len = strlen(msg);
        // 加锁,确保整个字符串写入过程不被打断
        pthread_mutex_lock(&write_mutex);
        while (cnt < len) {
            int res = write(fd, &msg[cnt], len - cnt);
            if (res != -1) {
                cnt += res;
            }
        }
        pthread_mutex_unlock(&write_mutex);
        sleep(1);
    }

    return NULL;
}

void* reader(void*) {
    char buff[1024];
    while (true) {
        read(fd, buff, sizeof(buff));
    }

    return NULL;
}

int main() {
    fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
        perror("error while opening port");
    }

    set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)

    // 初始化互斥锁
    pthread_mutex_init(&write_mutex, NULL);

    pthread_t reader_thread;
    pthread_attr_t reader_thread_attribute;
    pthread_attr_init(&reader_thread_attribute);
    pthread_create(&reader_thread, &reader_thread_attribute, reader, NULL);

    pthread_t writer_thread1;
    pthread_attr_t writer_thread_attribute1;
    pthread_attr_init(&writer_thread_attribute1);
    pthread_create(&writer_thread1, &writer_thread_attribute1, writer, (void*)"writer1");


    pthread_t writer_thread2;
    pthread_attr_t writer_thread_attribute2;
    pthread_attr_init(&writer_thread_attribute2);
    pthread_create(&writer_thread2, &writer_thread_attribute2, writer, (void*)"writer2");

    pthread_join(reader_thread, NULL);
    pthread_join(writer_thread1, NULL);
    pthread_join(writer_thread2, NULL);

    // 销毁互斥锁
    pthread_mutex_destroy(&write_mutex);
    close(fd);
}

关键说明

  • 新增全局互斥锁pthread_mutex_t write_mutex,每个写线程在开始写入前加锁,完成整个字符串写入后解锁,保证单条消息的发送连续性。
  • 读取线程不受影响,串口的读、写操作本身相互独立,无需与写操作共用同一锁(业务逻辑有特殊要求除外)。

内容的提问来源于stack exchange,提问作者Harry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:07:11