线程间之event

eventfd()创建了一个“eventfd对象”, 通过它能够实现用户态程序间。
此对象包含了一个被内核所维护的计数(uint64_t), 初始值由initval来决定。
eventfd在内核里的核心是一个计数器counter,它是一个uint64_t的整形变量counter,初始值为initval。

1.read(eventfd_read)

消费者需要对信号量进行down操作时,调用read从eventfd读即可。read返回值:

如果当前counter > 0,那么read返回counter值,并重置counter为0;

如果当前counter等于0,那么read 1)阻塞直到counter大于0;2)如果设置了NONBLOCK,那么返回-1,并设置errno为EAGAIN。
可以看到,eventfd实现的资源是一次性消耗品,只允许一次read。

2.write(eventfd_write)

生产者需要执行up操作时,调用write写一个64bit的整数value到eventfd即可。write返回值:

counter最大能存储的值是 0xffff ffff ffff fffe(以max表示此值),那么write尝试将value加到counter上,如果结果超过max,那么write一直阻塞直到有read操作发生,或者返回-1并设置errno为EAGAIN。
所以write可以多次连续调用,但read读一次即可清零。实质上它应该是一个二元信号量,只有0和非0两种状态。

*int eventfd(unsigned int initval, int flags);创建一个eventfd文件描述符
int eventfd_read(int fd, eventfd_t value); 向eventfd中写入一个值
int eventfd_write(int fd, eventfd_t value); 从eventfd中读出一个值

1、子线程多次写入多个值,主线程一次读出所有值的和

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>  

int main(int argc, char**argv[])
{
    int efd, j;
    uint64_t u;
    ssize_t s;
    
    if (argc < 2)
    {
        printf("number of argc is wrong!\n");
        return 0;
    }
    
    efd = eventfd(0,0);
    if (-1 == efd)
    {
        printf("failed to create eventfd\n");
    }
    
    switch(fork())
    {
        case 0:
        {
            for(j=1; j<argc;j++)
            {
                printf("child writing %s to efd\n", argv[j]);
                u = strtoull(argv[j], NULL, 0);
                s = write(efd, &u, sizeof(uint64_t));
                if (s!=sizeof(uint64_t))
                {
                    printf("write efd failed\n");
                }
            }
            printf("Child completed write loop\n");
            exit(0);
        }
        default:
            sleep(2);
            printf("Parents about to read\n");
            s = read(efd, &u, sizeof(uint64_t));
            if (s != sizeof(uint64_t))
            {
                printf("read efd failed\n");
            }
            printf("Parents first read %llu (0x%llx) from efd\n", u, u);
            exit(0);
        case -1:
        {
            printf("fork error\n");
        }
    }
    
    return 0;
}

二、eventfd可以被epoll监控, 一旦有状态变化,可以触发通知

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>  
#include <sys/epoll.h>  
#include <string.h>  
#include <pthread.h>  

int g_iEvtfd = -1;

void *eventfd_child_Task(void *pArg)
{
    uint64_t uiWrite = 1;
    
    while(1)
    {
        sleep(2);
        if (0 != eventfd_write(g_iEvtfd, uiWrite))
        {
            printf("child write iEvtfd failed\n");
        }    
    }

    return;
}

int main(int argc, char**argv[])
{
    int iEvtfd, j;
    uint64_t uiWrite = 1;
    uint64_t uiRead;
    ssize_t s;
    int iEpfd;
    struct epoll_event stEvent;
    int iRet = 0;
    struct epoll_event stEpEvent;
    pthread_t stWthread;
    
    iEpfd = epoll_create(1);
    if (-1 == iEpfd)
    {
        printf("Create epoll failed.\n");
        return 0;
    }
    
    iEvtfd = eventfd(0,0);
    if (-1 == iEvtfd)
    {
        printf("failed to create eventfd\n");
        return 0;
    }
    
    g_iEvtfd = iEvtfd;
    
    memset(&stEvent, 0, sizeof(struct epoll_event));
    stEvent.events = (unsigned long) EPOLLIN;
    stEvent.data.fd = iEvtfd;
    iRet = epoll_ctl(iEpfd, EPOLL_CTL_ADD, g_iEvtfd, &stEvent);
    if (0 != iRet)
    {
        printf("failed to add iEvtfd to epoll\n");
        close(g_iEvtfd);
        close(iEpfd);
        return 0;
    }
    
    iRet = pthread_create(&stWthread, NULL, eventfd_child_Task, NULL);
    if (0 != iRet)
    {
        close(g_iEvtfd);
        close(iEpfd);
        return;
    }
    
    for(;;)
    {
        iRet = epoll_wait(iEpfd, &stEpEvent, 1, -1);
        if (iRet > 0)
        {
            s = eventfd_read(iEvtfd, &uiRead);
            if (s != 0)
            {
                printf("read iEvtfd failed\n");
                break;
            }
            printf("Read %llu (0x%llx) from iEvtfd\n", uiRead, uiRead);
        }
    }
    
    close(g_iEvtfd);
    close(iEpfd);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 中的线程间通信(Thread Communication)是为了让多个线程之间能够协作、同步和共享数据。这是实现并发编程的关键,有助于提高程序的效率和响应性。Java 提供了几种机制来支持线程间通信: 1. **wait() 和 notify() 方法**:这两个方法定义在 `Object` 类中,用于线程间的等待-唤醒模式。一个线程调用 `wait()` 方法会释放对象锁并进入等待状态,而持有锁的其他线程可以通过 `notify()` 或 `notifyAll()` 来唤醒等待的线程。 2. **synchronized 关键字**:用于同步代码块或方法,保证在同一时间只有一个线程可以访问特定代码段。这对于共享资源的互斥访问至关重要。 3. **BlockingQueue**:Java集合框架中的非阻塞队列,如 `LinkedList`、`ArrayBlockingQueue` 等,允许线程之间通过队列传递消息或数据。 4. **CountDownLatch** 和 **CyclicBarrier**:前者是一个计数器,当所有等待的线程完成某个任务后才会继续;后者是一个屏障,线程们会在到达屏障点时一起停下来,然后继续执行。 5. **Semaphore**:信号量可以控制同时访问某个资源的线程数量,类似于一个票证系统。 6. **Future 和 Callable**:`Future` 接口用于异步执行任务的结果,`Callable` 提供了一个可以返回结果的线程,结合 `ExecutorService` 可以实现线程间的异步通信。 7. **EventListeners** 和 `java.util.Observer`:适用于事件驱动编程模式,当事件发生时通知感兴趣的监听者。 8. **Java Message Service (JMS)**:企业级的消息传递API,用于生产者和消费者之间的异步通信。 每个场景选择合适的通信机制能更好地管理并发和避免死锁等问题。如果你需要进一步了解这些内容,或者有具体使用场景的问题,可以提出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值