• 沉淀的梦想
    2019-10-07
    关于队列满的情况,额外加一个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;
    }
    展开

    作者回复: 不错的想法。

     2
     6
  • 安排
    2019-10-07
    1.没位置可用可以选择丢弃,取出来直接关闭,等待对方重连,或者先判断队列是否有位置,没位置的话直接就不取出套接字,让它留在内核队列中,让内核处理。
    2.存在竞态条件,需要加锁,不加锁可能大部分时间也能得到正确结果。
    
     2
  • Steiner
    2019-10-14
    请问block_queue_pop的pthread_cond_wait为什么要放在while而不是if中

    作者回复: 好问题。

    这是为了确保被pthread_cond_wait唤醒之后的线程,确实可以满足继续往下执行的条件。如果没有while循环的再次确认,可能直接就往下执行了。

     1
     1
  • gogo
    2019-10-09
    1. (just talk)可以拒绝 扩容 或者 加一个信号量来变为阻塞队列叭

    2. 绝大多数情况下这个值小于2000哇
    
     1
  • 刘丹
    2019-10-07
    block_queue_push未考虑队列满的情况,可以在本函数里先判断是否队列满了,如果满就按某个策略扩容,例如扩大1.5或2倍。扩容失败或者容量超过最大值,就返回失败。

    作者回复: 很好的方法。

    
     1
  • 超大红细胞
    2020-01-17
    每次添加 fd 之前,判断队列是否满了
     (blockQueue->rear + 1) % blockQueue->number == blockQueue->front
    如果满了可以采用类似 C++ vector 的扩容方式直接把线程池翻倍扩容,
    如果扩容前的 blockQueue->rear < blockQueue->front
    还需要将 0~blockQueue->rear 的 fd 移到 blockQueue->front~blockQueue->number-1 的后面,再将新的 fd 加入

    作者回复: 好办法

    
    
  • Geek_68d3d2
    2019-12-15
    线程里面 用完的fd好像都没有close掉吧

    作者回复: 可能的。

    
    
  • godtrue
    2019-11-24
    1:阻塞IO+多进程——实现简单,性能一般
    2:阻塞IO+多线程——相比于阻塞IO+多进程,减少了上下文切换所带来的开销,性能有所提高。
    3:阻塞IO+线程池——相比于阻塞IO+多线程,减少了线程频繁创建和销毁的开销,性能有了进一步的提高。
    
    
  • 阿尔卑斯
    2019-11-06
    我自定义阻塞队列(多了个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;
    展开

    作者回复: 可以的。

    
    
  • Steiner
    2019-10-21
    请问我想验证这个阻塞队列的正确性,那我的代码逻辑应该怎么写

    作者回复: 我的main函数已经在验证了,开多个线程,有往队列里扔对象的,有从队列里取对象处理的,看看这个程序是否可以处理高并发的场景,这就是你的代码逻辑需要关注的点。

    
    
  • Steiner
    2019-10-18
    请问thread_run函数里这个int fd = (int) arg 合法吗,我觉得是int fd = *(int *)arg吧

    作者回复: 合法的,传值还是传地址,是自己在程序里面定义的。

     1
    
  • 传说中的成大大
    2019-10-14
    pthread_create(&(thread_array[i].thread_tid), NULL, &thread_run, (void *) &blockQueue);
    pthread_create函数调用这里有问题 应该是
    void* thread_run(void *arg) {
        pthread_t tid = pthread_self();
        pthread_detach(tid);

        block_queue *blockQueue = (block_queue *) arg;
        while (1) {
            int fd = block_queue_pop(blockQueue);
            printf("get fd in thread, fd==%d, tid == %d", fd, tid);
            loop_echo(fd);
        }
    }
    我编辑代码的时候总是报错 后来通过man函数和编译才差出来
    展开

    作者回复: 提个MR或者issue?我看大家都正常啊,你的系统是啥?

    
    
  • 传说中的成大大
    2019-10-14
    看了git上的代码,我又思考了第一题 一般来说肯定数量一定会被限定,不可能无线扩容,并且还要回收利用,比如某个连接关闭了,就应该将其移除,而不是再对其进行读写等操作,不然会浪费很多资源,还容易出现很多莫名其妙的tcp层面错误,最简单的例子 很多游戏都设置了一个排队,例如之前看见英雄联盟在登录界面的排队,还有魔兽也有类似的操作,所以如果本来描述符数量已经设置到合理大小了就不要再扩容而是采取排队进入的方式

    作者回复: 你的例子是说accetpor不要无限制的接收客户端的连接,相当于做了一个限流,这也是比较常见的一种手段。

    
    
  • 传说中的成大大
    2019-10-14
    第一问 连接字队列不可能说无限扩容,当某个连接断开时应该是要从连接字队列里面移除
    第二问 那是因为没有出现多线程竞争的情况也就是异步事件,也可以通过加锁来解决异步问题

    作者回复: 第一个问题可以提个MR给我哈

    
    
  • 赖阿甘
    2019-10-09
    分离线程中的“而一个分离的线程不能被其他线程杀死或回收资源。”这句话有误?虽然你调用pthread_detach(),但是其他线程还是可以通过pthread_cancel()结束这个线程吧!还望老师指点

    作者回复: 我理解pthread_detach以后就不能了,你可以尝试一下。

    
    
  • 我来也
    2019-10-07
    1.队列满可以关闭fd拒绝服务,也可以扩容等待队列,但都不太好。毕竟一个线程服务一个socket的开销还是有点大。还是多路复用加异步比较划算。
    2.就是并发会产生不确定性。在这个循环次数从1000变大些,出现的概率可能就更大了。
    
    
  • 程序水果宝
    2019-10-07
    thread_array = calloc(THREAD_NUMBER, sizeof(Thread));这个Thread是哪里来的,是C库的线程结构体吗?

    作者回复: typedef struct {
        pthread_t thread_tid; /* thread ID */
        long thread_count; /* # connections handled */
    } Thread;

    因为篇幅原因,不是所有代码都放到文稿里了,请到这里看详细的代码
    https://github.com/froghui/yolanda

     2
    
我们在线,来聊聊吧