线程并发,用标志位同步时要小心

本文探讨了在多线程环境中使用标志位进行同步时可能遇到的问题,特别是当多个线程并发调用同一函数时,如何确保关键代码段正确执行。通过引入互斥锁(mutex)来解决线程安全问题。
线程并发,用标志位同步时要小心

static INT bUseCount = 0;

static void socket_startup()
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2,2);

    if (bUseCount++ == 0) {
        WSAStartup(wVersionRequested, &wsaData);
    }
}

int socket_create(int domain, int type)
{
    socket_startup();
    return socket(domain, type, 0);
}

void proc_0(void * params)
{
    int fd_listen;
    fd_listen = socket_create(1, SOCK_STREAM);
    ...
}


_beginthread(proc_0, 0, 0);
_beginthread(proc_1, 0, 0);
_beginthread(proc_2, 0, 0);

三个线程并发,调用socket之前WSAStartup可能未执行!
对变量的访问加锁可解决这个问题
mutex.Lock();
if (bUseCount++ == 0) {
    WSAStartup(wVersionRequested, &wsaData);
}
mutex.Unlock();
<think> 我们讨论的是在AT指令控制模型中,命令发送者线程(即提交AT指令请求的线程)需要同步等待结果的情况。在之前的异步回调设计中,请求是通过回调函数异步处理的。但有些场景下,调用者可能需要同步等待结果(例如在某些必须按顺序执行的操作中)。 ### 线程同步等待的原理 当命令发送者线程需要同步等待AT指令的响应,我们需要一种机制让该线程阻塞,直到收到响应或超。常见的实现方式是使用**条件变量(Condition Variable)** 或**信号量(Semaphore)**。 #### 使用条件变量实现同步等待 条件变量通常与互斥锁(Mutex)配合使用。基本流程如下: 1. **发送请求前**:创建一个条件变量和互斥锁,并初始化一个状态标志(如`response_received`)为0。 2. **发送请求**:将请求提交到队列,同传递条件变量和状态标志的引用。 3. **等待响应**:线程在条件变量上等待,直到状态标志被设置或超。 4. **响应处理**:当AT指令的响应返回(在另一个线程,如串口接收线程),设置状态标志并通知条件变量。 5. **唤醒等待线程**:等待的线程被唤醒,检查状态标志,获取响应结果。 #### 使用信号量实现同步等待 信号量可以看作是一个计数器,它控制多个线程对共享资源的访问。在同步等待中,我们可以使用一个初始值为0的信号量: 1. **发送请求前**:初始化一个信号量(初始值为0)。 2. **发送请求**:提交请求,并将信号量作为用户数据传递给回调函数。 3. **等待信号量**:发送线程调用信号量的`wait`操作,阻塞直到信号量被释放。 4. **响应处理**:当回调函数被调用,释放信号量(`post`操作)。 5. **唤醒**:发送线程被唤醒,从回调函数设置的结果中获取数据。 ### 问题 1. **死锁风险**:如果响应没有到来,或者回调函数没有被调用,等待线程将永远阻塞。因此必须设置超机制。 2. **资源消耗**:每个同步请求都需要一个条件变量或信号量,在大量并发请求可能消耗较多资源。 3. **复杂性**:需要小心处理线程同步,避免竞态条件。 ### 解决方案 我们可以扩展之前的异步回调机制,增加同步等待的支持。具体设计如下: #### 数据结构扩展 ```c typedef struct { int completed; // 请求是否完成 int success; // 请求结果:成功或失败 char response[256]; // 存储响应 pthread_mutex_t mutex; // 互斥锁 pthread_cond_t cond; // 条件变量 } SyncRequestContext; ``` #### 同步请求函数 ```c int at_send_command_sync(AT_CommandContext *ctx, const char *cmd, char *response_buf, int buf_size, int timeout_ms) { SyncRequestContext sync_ctx; sync_ctx.completed = 0; pthread_mutex_init(&sync_ctx.mutex, NULL); pthread_cond_init(&sync_ctx.cond, NULL); // 定义回调函数,用于设置结果并通知 void callback(int success, const char *response, void *user_data) { SyncRequestContext *sync = (SyncRequestContext *)user_data; pthread_mutex_lock(&sync->mutex); sync->success = success; strncpy(sync->response, response, sizeof(sync->response)-1); sync->response[sizeof(sync->response)-1] = '\0'; sync->completed = 1; pthread_cond_signal(&sync->cond); // 通知等待线程 pthread_mutex_unlock(&sync->mutex); } // 提交异步请求,将sync_ctx作为user_data传递 if (!at_submit_request(ctx, cmd, callback, &sync_ctx, timeout_ms)) { pthread_mutex_destroy(&sync_ctx.mutex); pthread_cond_destroy(&sync_ctx.cond); return 0; // 提交失败 } // 等待响应 pthread_mutex_lock(&sync_ctx.mutex); struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_ms / 1000; ts.tv_nsec += (timeout_ms % 1000) * 1000000; if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } while (!sync_ctx.completed) { if (pthread_cond_timedwait(&sync_ctx.cond, &sync_ctx.mutex, &ts) == ETIMEDOUT) { break; // 超 } } int completed = sync_ctx.completed; int success = sync_ctx.success; if (completed && success && response_buf) { strncpy(response_buf, sync_ctx.response, buf_size-1); response_buf[buf_size-1] = '\0'; } pthread_mutex_unlock(&sync_ctx.mutex); pthread_mutex_destroy(&sync_ctx.mutex); pthread_cond_destroy(&sync_ctx.cond); return completed ? (success ? 1 : -1) : 0; // 1:成功, -1:失败, 0:超 } ``` #### 使用示例 ```c char response[256]; int ret = at_send_command_sync(&at_ctx, "AT+CSQ\r\n", response, sizeof(response), 1000); if (ret == 1) { printf("成功: %s\n", response); } else if (ret == -1) { printf("失败: %s\n", response); } else { printf("超\n"); } ``` ### 注意事项 1. **超处理**:使用`pthread_cond_timedwait`避免永久阻塞。 2. **内存安全**:回调函数中复制响应字符串确保不越界。 3. **线程安全**:对共享结构`SyncRequestContext`的操作必须加锁。 4. **资源释放**:在函数退出前销毁互斥锁和条件变量,避免资源泄漏。 ### 性能优化 1. **复用同步上下文**:在频繁使用同步请求的场景,可以设计一个同步上下文池,避免重复初始化和销毁。 2. **避免在回调中长间操作**:回调函数应尽快完成,只设置结果并通知,不要进行复杂处理。 ### 总结 通过条件变量和回调函数的结合,我们实现了AT指令的同步等待机制。该机制允许调用线程阻塞直到收到响应或超,同保持了与异步机制的兼容性。这种设计在需要同步等待的场景(如初始化流程)中非常有用。 §§相关问题§§ 1. 在实操作系统中如何实现同步等待机制? 2. 如何处理多个同步请求同等待的情况? 3. 同步等待机制对系统响应间有何影响? 4. 如何避免同步等待导致的线程阻塞问题? </think>### 命令发送者线程同步等待机制 在AT指令控制系统中,命令发送者线程同步等待是一种常见的需求,特别是在需要确保指令顺序执行或获取即结果的场景中。以下是该机制的原理、问题及解决方案的详细说明: #### 原理说明 命令发送者线程同步等待的核心原理是**线程阻塞与唤醒机制**,通过同步原语实现线程间的协调: ```mermaid sequenceDiagram participant Sender as 发送线程 participant Queue as 请求队列 participant Worker as 工作线程 Sender->>Queue: 提交请求(带同步信号量) activate Sender Sender->>Sender: sem_wait()阻塞 Queue->>Worker: 分配请求 Worker->>Worker: 执行AT指令 Worker->>Sender: sem_post()唤醒 deactivate Sender ``` 1. **信号量机制** 使用POSIX信号量实现同步控制: ```c #include <semaphore.h> typedef struct { char command[64]; sem_t completion_sem; // 完成信号量 int result; // 执行结果 char response[256]; // 响应数据 } SyncATRequest; ``` 2. **同步请求流程**: - 发送线程创建请求对象并初始化信号量 `sem_init(&req.completion_sem, 0, 0)` - 提交请求到队列后调用 `sem_wait()` 阻塞自身 - 工作线程完成AT指令后设置结果并调用 `sem_post()` - 发送线程被唤醒后读取响应数据 #### 关键问题及解决方案 1. **线程阻塞问题** **问题**:同步等待导致发送线程无法响应其他任务 **解决方案**: ```c // 带超的信号量等待 struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 2; // 2秒超 int ret = sem_timedwait(&req.completion_sem, &ts); if (ret == -1 && errno == ETIMEDOUT) { // 处理超逻辑 } ``` 2. **资源竞争问题** **问题**:多线程提交请求导致数据竞争 **解决方案**:使用互斥锁保护请求队列 ```c pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; void submit_request(SyncATRequest* req) { pthread_mutex_lock(&queue_mutex); // 安全操作队列 pthread_mutex_unlock(&queue_mutex); } ``` 3. **内存泄漏风险** **问题**:请求对象生命周期管理不当 **解决方案**:采用对象池模式 ```c #define SYNC_REQ_POOL_SIZE 10 SyncATRequest req_pool[SYNC_REQ_POOL_SIZE]; sem_t pool_sem; // 初始值为SYNC_REQ_POOL_SIZE SyncATRequest* alloc_request() { sem_wait(&pool_sem); // 获取资源 // 返回空闲请求对象 } void free_request(SyncATRequest* req) { sem_post(&pool_sem); // 释放资源 } ``` 对象池工作流程: ```mermaid graph LR A[请求分配] --> B{池中有资源?} B -- 是 --> C[分配对象] B -- 否 --> D[等待信号量] C --> E[使用对象] E --> F[释放回池] F --> G[释放信号量] ``` #### 完整实现示例 ```c typedef struct { char command[64]; sem_t completion_sem; int result; char response[256]; } SyncATRequest; // 请求对象池 SyncATRequest sync_req_pool[10]; sem_t pool_sem; void init_sync_system() { sem_init(&pool_sem, 0, 10); // 初始化10个资源 for(int i=0; i<10; i++) { sem_init(&sync_req_pool[i].completion_sem, 0, 0); } } SyncATRequest* alloc_sync_request() { sem_wait(&pool_sem); // 等待可用资源 for(int i=0; i<10; i++) { if(sync_req_pool[i].command[0] == '\0') { return &sync_req_pool[i]; } } return NULL; } int send_at_command_sync(const char* cmd, char* response, int timeout_ms) { SyncATRequest* req = alloc_sync_request(); if(!req) return -1; // 资源不足 strncpy(req->command, cmd, sizeof(req->command)); // 提交到工作队列 pthread_mutex_lock(&queue_mutex); enqueue_request(req); pthread_mutex_unlock(&queue_mutex); // 等待完成(带超) struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += (timeout_ms % 1000) * 1000000; ts.tv_sec += timeout_ms / 1000 + ts.tv_nsec / 1000000000; ts.tv_nsec %= 1000000000; int ret = sem_timedwait(&req->completion_sem, &ts); if(ret == -1 && errno == ETIMEDOUT) { // 超处理 } // 复制结果 if(req->result == 0) { strncpy(response, req->response, 256); } // 重置并释放请求对象 memset(req, 0, sizeof(SyncATRequest)); sem_post(&pool_sem); return req->result; } // 在工作线程中 void process_sync_request(SyncATRequest* req) { send_at_command(req->command); // 模拟响应处理 if(strstr(serial_buffer, "OK")) { req->result = 0; strncpy(req->response, serial_buffer, sizeof(req->response)); } else { req->result = -1; } sem_post(&req->completion_sem); // 唤醒发送线程 } ``` #### 性能优化建议 1. **双队列设计**: ```c // 分离同步和异步队列 Queue sync_queue; // 同步请求高优先级 Queue async_queue; // 异步请求低优先级 ``` 2. **线程优先级调整**: ```c // 设置工作线程优先级 struct sched_param param; param.sched_priority = 90; // 高优先级 pthread_setschedparam(worker_thread, SCHED_FIFO, &param); ``` 3. **响应缓存机制**: ```c // 对频繁请求的指令缓存结果 char cached_csq[32]; time_t last_csq_time; int get_signal_strength() { if(time(NULL) - last_csq_time < 60) { return atoi(cached_csq); // 返回缓存值 } // 否则执行同步请求... } ``` #### 应用场景分析 1. **适合同步等待的场景**: - 设备初始化流程 - 关键配置写入操作 - 实性要求高的状态查询 2. **应避免同步的场景**: - 后台数据采集 - 周期性的状态监测 - 非关键日志记录 > 在移动通信模块中,同步等待机制在发送短信(AT+CMGS)和拨号(ATD)等关键操作中尤为重要,可确保指令序列的严格顺序执行[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值