26 | 使用阻塞I/O和线程模型:换一种轻量的方式
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
本文介绍了使用线程模型处理多用户连接请求的方法,并详细介绍了POSIX线程模型的相关函数。通过一个简单的例子程序展示了如何使用线程模型处理多用户连接请求,以及如何在服务器端程序中创建新线程来处理每个连接,实现了多个并发连接的处理。文章还介绍了如何使用预创建线程池的方式来优化服务器端程序,减少线程创建和销毁的开销。关键的连接字队列设计和实现也得到了详细讲解,包括锁和条件变量的使用。通过代码示例和解释,读者可以快速了解线程模型的使用方法和优势,以及在网络编程实践中的应用。总的来说,本文对于需要处理多用户连接请求的网络编程实践具有一定的参考价值。文章还提出了思考题,鼓励读者深入思考和交流。
《网络编程实战》,新⼈⾸单¥59
全部留言(41)
- 最新
- 精选
- 丷王传奇丷第二题:可能会得到比正确的值小的值 i++大致分为三个步骤: 1、从内存读出i的值到寄存器 2、操作寄存器加1 3、将寄存器值写到i内存 多个线程去操作同一个全局变量的时候,可能某个线程在第二步的时候切换到另一个线程,这样就导致少加了。比如线程A 在i=1 的时候在第二步,这个时候寄存器加1值为2,在这个时候切换到线程B ,由于线程A还没有把2写到i里面,使用B读出来还是1,自增写到i里面,i为2,在切换到线程A,线程A将寄存器里面的2写到i,这样就少加了一次。
作者回复: 多线程程序设计的常见坑。解释到位,赞。
2020-02-13420 - 忆水寒第一道,其实这里使用的生产者-消费者模型,可以使用扩容策略解决fd存放的问题。 第二题,在并发场景下很容易造成计算结果的不准确。因为这里面是两个线程各执行1000次。实际上很大结果是少于2000的结果。解决方法可以加上锁或volitate关键字(解决可见行问题)。
作者回复: 👍
2020-03-15416 - 沉淀的梦想关于队列满的情况,额外加一个cond,表示队列未满条件就可以了。 typedef struct { int number; int *fd; int front; int rear; // 队列中当前元素数目 int count; pthread_mutex_t mutex; pthread_cond_t not_empty; // 队列未满条件 pthread_cond_t not_full; } block_queue; void block_queue_init(block_queue *blockQueue, int number) { blockQueue->number = number; blockQueue->fd = calloc(number, sizeof(int)); blockQueue->count = blockQueue->front = blockQueue->rear = 0; pthread_mutex_init(&blockQueue->mutex, NULL); pthread_cond_init(&blockQueue->not_empty, NULL); pthread_cond_init(&blockQueue->not_full, NULL); } void block_queue_push(block_queue *blockQueue, int fd) { pthread_mutex_lock(&blockQueue->mutex); while (blockQueue->count == blockQueue->number){ //队列满 pthread_cond_wait(&blockQueue->not_full, &blockQueue->mutex); } blockQueue->fd[blockQueue->rear] = fd; if (++blockQueue->rear == blockQueue->number) { blockQueue->rear = 0; } blockQueue->count++; printf("push fd %d", fd); pthread_cond_signal(&blockQueue->not_empty); pthread_mutex_unlock(&blockQueue->mutex); } int block_queue_pop(block_queue *blockQueue) { pthread_mutex_lock(&blockQueue->mutex); while (blockQueue->front == blockQueue->rear) // 空队列 pthread_cond_wait(&blockQueue->not_empty, &blockQueue->mutex); int fd = blockQueue->fd[blockQueue->front]; if (++blockQueue->front == blockQueue->number) { blockQueue->front = 0; } blockQueue->count--; printf("pop fd %d", fd); pthread_cond_signal(&blockQueue->not_full); // 解锁 pthread_mutex_unlock(&blockQueue->mutex); return fd; }
作者回复: 不错的想法。
2019-10-07516 - Steiner请问block_queue_pop的pthread_cond_wait为什么要放在while而不是if中
作者回复: 好问题。 这是为了确保被pthread_cond_wait唤醒之后的线程,确实可以满足继续往下执行的条件。如果没有while循环的再次确认,可能直接就往下执行了。
2019-10-14212 - 刘丹block_queue_push未考虑队列满的情况,可以在本函数里先判断是否队列满了,如果满就按某个策略扩容,例如扩大1.5或2倍。扩容失败或者容量超过最大值,就返回失败。
作者回复: 很好的方法。
2019-10-075 - 林燕老师,请问一下,我跑了thread02这个程序。在没有客户端连上来的情况下,我发现四个子线程都执行到了block_queqe_pop函数里面,并都成功执行完pthread_mutex_lock,阻塞在后面的while循环里面。这里我想问的是四个子线程,不是最多只应该有一个能拿到互斥量,其他三个都应该阻塞在pthread_mutex_lock函数中么?
作者回复: 问题的关键是pthread_cond_wait这个函数,事实上,这个函数使得当前线程进入休眠,并且释放当前线程持有的互斥锁。而当调用线程后来从pthread_cond_wait返回时,该线程再次持有该互斥锁。这就是为什么四个子线程都可以跑完pthread_mutex_lock,然后都在pthread_cond_wait这个函数中休眠等待的原因。
2021-04-253 - 愚笨的老鼠这个线程函数里面,没有调用close关闭connfd,导致,很多很多socket状态不对,没有正常进入time_wait状态 tcp4 0 0 127.0.0.1.43211 127.0.0.1.53703 CLOSE_WAIT tcp4 0 0 127.0.0.1.53703 127.0.0.1.43211 FIN_WAIT_2 tcp4 0 0 127.0.0.1.43211 127.0.0.1.53695 CLOSE_WAIT tcp4 0 0 127.0.0.1.53695 127.0.0.1.43211 FIN_WAIT_2
作者回复: 确实如此,需要在循环函数里处理完加上close(fd)的操作。
2020-03-203 - 愚笨的老鼠有个地方不太理解,一开始初始状态下,线程pop加锁了,为什么,有accept时候,执行push的时候获取锁怎么能成功呢,不是锁已经被pop用了,还没释放呢
作者回复: 问题的关键在于pthread_cond_wait的语义,在消费者线程中,通过wait等待连接处理,这个时候accept之后执行 push获取锁是可以的,并且通过signal的方式让消费者线程苏醒过来处理。 在绝大部分时刻,消费者线程都是在wait状态的,push一开始的lock锁只是一个非常短暂的check动作。
2020-03-2122 - supermouse老师我想问一下,线程的创建销毁和进程的创建销毁哪个开销比较大?
作者回复: 我觉得是线程。
2020-02-2442 - 阿尔卑斯我自定义阻塞队列(多了个cnt计数元素个数,和条件变量分开来,实现起来清晰明了简单多了) typedef struct { int size; //队列容量 int *pFd; //存储队列元素,动态分配 int cnt; //队列当前元素个数 int pushIdx; //入队元素索引 int popIdx; //出对元素索引 pthread_mutex_t mutex; //锁 pthread_cond_t noFull; //非满,即队列元素个数cnt从size值,变成size - 1时的触发条件 pthread_cond_t noEmpty;//非空,即队列元素个数cnt从0值,变成1时的触发条件 }BlockQueue;
作者回复: 可以的。
2019-11-062