Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在Linux上等待多个条件变量而不进行不必要的睡眠?_C_Multithreading_Pthreads_Conditional Statements_Scheduling - Fatal编程技术网

C 在Linux上等待多个条件变量而不进行不必要的睡眠?

C 在Linux上等待多个条件变量而不进行不必要的睡眠?,c,multithreading,pthreads,conditional-statements,scheduling,C,Multithreading,Pthreads,Conditional Statements,Scheduling,我正在编写一个对延迟敏感的应用程序,实际上它希望同时等待多个条件变量。我以前读过几种在Linux上获得此功能的方法(显然这是Windows上内置的),但它们似乎都不适合我的应用程序。我知道的方法有: 让一个线程等待您要等待的每个条件变量,当唤醒它时,它将向您等待的单个条件变量发出信号 通过定时等待循环多个条件变量 将虚拟字节写入文件或管道,并轮询这些文件或管道 #1和2不合适,因为它们会导致不必要的睡眠。对于#1,您必须等待虚拟线程唤醒,然后向真实线程发送信号,然后让真实线程唤醒,而不是让真实线

我正在编写一个对延迟敏感的应用程序,实际上它希望同时等待多个条件变量。我以前读过几种在Linux上获得此功能的方法(显然这是Windows上内置的),但它们似乎都不适合我的应用程序。我知道的方法有:

  • 让一个线程等待您要等待的每个条件变量,当唤醒它时,它将向您等待的单个条件变量发出信号

  • 通过定时等待循环多个条件变量

  • 将虚拟字节写入文件或管道,并轮询这些文件或管道

  • #1和2不合适,因为它们会导致不必要的睡眠。对于#1,您必须等待虚拟线程唤醒,然后向真实线程发送信号,然后让真实线程唤醒,而不是让真实线程从一开始就醒来——在这方面花费的额外调度程序量实际上对我的应用程序很重要,我不希望使用成熟的RTO#2更糟糕的是,您可能会花费N*超时时间睡眠,或者您的超时时间将为0,在这种情况下,您将永远不会睡眠(无休止地消耗CPU和饥饿的其他线程也很糟糕)

    对于#3,管道是有问题的,因为如果被“发信号”的线程很忙甚至崩溃(事实上,我处理的是单独的进程而不是线程——互斥体和条件将存储在共享内存中),那么写入线程将被卡住,因为管道的缓冲区将满,其他客户端也一样。文件是有问题的,因为应用程序运行的时间越长,文件就会无限增长


    有更好的方法吗?想知道适用于Solaris的答案吗。

    如果您谈论的是POSIX线程,我建议您使用单个条件变量和事件标志数或类似的东西。其思想是使用对等condvar互斥来保护事件通知。无论如何,您需要在cond_wait()退出后检查事件。这是我的老代码,可以从我的培训中说明这一点(是的,我检查了它是否运行,但请注意,它是在一段时间前准备好的,对于新手来说很匆忙)

    #包括
    #包括
    #包括
    静态pthread_cond_t var;
    静态pthread_mutex_t mtx;
    未签名事件\u标志=0;
    #定义标志\u事件\u 1
    #定义标志\u事件\u 2
    无效信号_1()
    {
    pthread_mutex_lock(&mtx);
    事件|标志|=标志(事件)1;
    pthread_cond_信号(&var);
    pthread_mutex_unlock(&mtx);
    }
    无效信号_2()
    {
    pthread_mutex_lock(&mtx);
    事件|标志|=标志(事件)2;
    pthread_cond_信号(&var);
    pthread_mutex_unlock(&mtx);
    }
    void*处理程序(void*)
    {
    //只有当我们等待或处理接收到的事件时,互斥锁才会解锁。
    pthread_mutex_lock(&mtx);
    //这里应该是真实代码中的竞争条件预防。
    而(1)
    {
    如果(事件标志)
    {
    未签名副本=事件标志;
    //我们在处理接收到的事件时解锁互斥锁。
    pthread_mutex_unlock(&mtx);
    if(复制并标记事件1)
    {
    printf(“事件1\n”);
    复制^=标志事件1;
    }
    if(复制并标记事件2)
    {
    printf(“事件2\n”);
    复制^=标志事件2;
    //让事件2成为“退出”信号。
    //在这种情况下,为了保持一致性,我们使用锁定互斥来中断。
    pthread_mutex_lock(&mtx);
    打破
    }
    //注意,我们应该在迭代结束时锁定互斥锁。
    pthread_mutex_lock(&mtx);
    }
    其他的
    {
    //互斥锁已锁定。在我们等待时,它已解锁。
    pthread_cond_wait(&var,&mtx);
    //互斥锁已锁定。
    }
    }
    //…我们快死了。
    pthread_mutex_unlock(&mtx);
    }
    int main()
    {
    pthread_mutex_init(&mtx,NULL);
    pthread_cond_init(&var,NULL);
    pthread_t id;
    pthread_创建(&id,NULL,handler,NULL);
    睡眠(1);
    信号_1();
    睡眠(1);
    信号_1();
    睡眠(1);
    信号_2();
    睡眠(1);
    pthread_join(id,NULL);
    返回0;
    }
    
    对于等待多个条件变量,如果您感兴趣,可以将Solaris的一个实现移植到Linux:

    您的#3选项(将虚拟字节写入文件或管道,并对其进行轮询)在Linux上有更好的选择:

    使用
    eventfd
    可以使用内核内无符号64位计数器,而不是有限大小的缓冲区(如管道中)或无限增长的缓冲区(如文件中)。8字节的
    写入
    向计数器添加一个数字;8字节的
    read
    要么将计数器归零并返回其以前的值(无
    EFD\u信号量
    ),要么将计数器减量1并返回1(有
    EFD\u信号量
    )。当计数器非零时,文件描述符被认为是轮询函数可读的(
    select
    poll
    epoll


    即使计数器接近64位限制,如果使文件描述符非阻塞,写入
    操作也将失败,并显示
    EAGAIN
    。计数器为零时,
    read
    也会发生同样的情况。

    如果您希望在POSIX条件变量同步模型下获得最大的灵活性,则必须避免编写仅通过公开条件变量向其用户传递事件的模块。(然后您基本上重新发明了一个信号量。)

    活动模块的设计应确保其接口通过注册的函数提供事件的回调通知:如果需要,还应确保可以注册多个回调

    多个模块的客户端向每个模块注册回调。这些都可以路由到一个公共位置,在那里它们锁定相同的互斥锁、更改某些状态、解锁并命中相同的条件变量

    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    
    static pthread_cond_t var;
    static pthread_mutex_t mtx;
    
    unsigned event_flags = 0;
    #define FLAG_EVENT_1    1
    #define FLAG_EVENT_2    2
    
    void signal_1()
    {
        pthread_mutex_lock(&mtx);
        event_flags |= FLAG_EVENT_1;
        pthread_cond_signal(&var);
        pthread_mutex_unlock(&mtx);
    }
    
    void signal_2()
    {
        pthread_mutex_lock(&mtx);
        event_flags |= FLAG_EVENT_2;
        pthread_cond_signal(&var);
        pthread_mutex_unlock(&mtx);
    }
    
    void* handler(void*)
    {
        // Mutex is unlocked only when we wait or process received events.
        pthread_mutex_lock(&mtx);
    
        // Here should be race-condition prevention in real code.
    
        while(1)
        {
            if (event_flags)
            {
                unsigned copy = event_flags;
    
                // We unlock mutex while we are processing received events.
                pthread_mutex_unlock(&mtx);
    
                if (copy & FLAG_EVENT_1)
                {
                    printf("EVENT 1\n");
                    copy ^= FLAG_EVENT_1;
                }
    
                if (copy & FLAG_EVENT_2)
                {
                    printf("EVENT 2\n");
                    copy ^= FLAG_EVENT_2;
    
                    // And let EVENT 2 to be 'quit' signal.
                    // In this case for consistency we break with locked mutex.
                    pthread_mutex_lock(&mtx);
                    break;
                }
    
                // Note we should have mutex locked at the iteration end.
                pthread_mutex_lock(&mtx);
            }
            else
            {
                // Mutex is locked. It is unlocked while we are waiting.
                pthread_cond_wait(&var, &mtx);
                // Mutex is locked.
            }
        }
    
        // ... as we are dying.
        pthread_mutex_unlock(&mtx);
    }
    
    int main()
    {
        pthread_mutex_init(&mtx, NULL);
        pthread_cond_init(&var, NULL);
    
        pthread_t id;
        pthread_create(&id, NULL, handler, NULL);
        sleep(1);
    
        signal_1();
        sleep(1);
        signal_1();
        sleep(1);
        signal_2();
        sleep(1);
    
        pthread_join(id, NULL);
        return 0;
    }