2013-11-02 49 views
2

我希望通過eventfd在線程之間傳輸數據。所以我寫了下面的代碼來滿足我的需要。我的主機操作系統是openSUSE v12.3 64位。通過eventfd在線程之間傳輸數據

#include "stdio.h" 
#include "unistd.h" 
#include "pthread.h" 
#include "sys/eventfd.h" 
#include "sys/epoll.h" 
#define nil NULL 

int efd = -1; 

void* read_thread(void* arg) { 

     int ret = 0; 
    uint64_t count = 0; 
    int ep_fd = -1; 
    struct epoll_event events[10]; 

    (void)(arg); 
    if (efd < 0) { 
     printf("efd not inited.\n"); 
     return nil; 
    } 

    ep_fd = epoll_create(1024); 
    if (ep_fd < 0) { 
     perror("epoll_create fail: "); 
     return nil; 
    } 

    { 
     struct epoll_event read_event; 

     read_event.events = EPOLLIN; 
     read_event.data.fd = efd; 

     ret = epoll_ctl(ep_fd, EPOLL_CTL_ADD, efd, &read_event); 
     if (ret < 0) { 
      perror("epoll ctl failed:"); 
      return nil; 
     } 
    } 

    while (1) { 
     ret = epoll_wait(ep_fd, events, 10, 5000); 
     if (ret > 0) { 
      int i = 0; 
      for (i = 0; i < ret; i++) { 
       if (events[i].events & (EPOLLHUP | EPOLLERR)) { 
        printf("epoll eventfd has epoll hup.\n"); 
        break; 
       } else if (events[i].events & EPOLLIN) { 
        int event_fd = events[i].data.fd; 
        ret = eventfd_read(event_fd, &count); 
        if (ret < 0) { 
         perror("read fail:"); 
         break; 
        } else { 
         printf("read %llu\n", count); 
        } 
       } 
      } 
     } else if (ret == 0) { 
      break; 
     } else { 
      perror("epoll wait error:"); 
      break; 
     } 
    } 

    close(ep_fd); 
    return nil; 
} 

int main(int argc, char *argv[]) { 

    pthread_t pid = 0; 
    uint64_t count = 0; 
    int ret = 0; 
    int i = 0; 

    efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 
    if (efd < 0) { 
     perror("eventfd failed."); 
     return -1; 
    } 

    ret = pthread_create(&pid, NULL, read_thread, NULL); 
    if (ret < 0) { 
     perror("pthread create:"); 
     close(efd); 
     return -2; 
    } 

    for (i = 0; i < 5; i++) { 
     count = i + 1; 
     ret = eventfd_write(efd, count); 
     if (ret < 0) { 
      perror("write event fd fail:"); 
      break; 
     } else { 
      printf("write %llu\n", count); 
     } 
    } 

    sleep(3); 

    pthread_join(pid, NULL); 
    close(efd); 
    efd = -1; 

    return 0; 
} 

我認爲eventfd的行爲應該像一個管道。所以該程序的輸出結果可以如下:

寫入1讀1寫2讀2寫3讀3寫入4讀4寫5 讀5

然而,它的實際的結果是:

寫入1個寫入2寫入3寫入寫入4 5讀取15

寫1只寫2寫3寫4寫5閱讀1閱讀14

一些好人能告訴我爲什麼輸出結果是輸入結果的總和?

我無法通過線程之間的eventfd正確傳輸數據。我也不喜歡管道。如何在線程之間正確地傳輸數據?

+1

是不是指流程?爲什麼在共享內存時爲線程造成這種開銷? – Leeor

+2

簡而言之,因爲線程受到OS調度程序的突發事件的影響。僅僅因爲fd變得可讀並不意味着該線程將被立即計劃運行。在處理線程時,不要讓你的代碼以你的思維方式爲前提,而應該安排它們。如果你想同步線程,那麼使用synchonization機制,例如互斥體,信號量,cond var等。 – Duck

+0

您對'pthread_create'的錯誤檢查和錯誤處理不正確。看到一個當代的[linux手冊頁](http://man7.org/linux/man-pages/man3/pthread_create.3.html)以獲得正確的錯誤處理。 – pilcrow

回答

5

eventfd使用由內核維護的計數器,該計數器以提供給eventfd()的值開始。這個計數器通過write()函數遞增'sent',所以如果你創建的eventfd初始值爲1,然後寫入()2,那麼你讀取的值將是3.如果你然後寫入()5 ,read()值將是8等等。

eventfd通常由事件驅動模式中的線程使用,但您需要使用隊列或其他容器來傳遞實際值,或者檢出signalfd(),它可用於通過事件驅動模式sigqueue()。