多线程向同一串口写入数据,该操作是否具备线程安全性?
多线程写入串行端口的线程安全性问题
问题核心
你的代码中多个写线程直接调用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
相关产品推荐
相关产品推荐

