2013-02-08 106 views
0

在我的C++程序中,我使用lio_listio調用一次發送很多(多達幾百)個寫請求。之後,我做了一些計算,當我完成後,我需要等待所有未完成的請求完成,然後才能提交下一批請求。我怎樣才能做到這一點?lio_listio:如何等待所有請求完成?

現在,我只是在一個循環中調用aio_suspend,每次調用一個請求,但這看起來很難看。看起來我應該使用lio_listiostruct sigevent *sevp參數。我目前的猜測是我應該這樣做:

  • 在主線程中,創建一個互斥鎖並在鎖定之前調用lio_listio
  • 在對lio_listio的調用中,指定解鎖此互斥鎖的通知函數/信號處理函數。

這應該給我想要的行爲,但它會工作可靠嗎?是否允許從信號處理程序上下文中操作互斥鎖?我讀到pthread互斥鎖可以提供錯誤檢測,並且如果您嘗試再次從同一個線程鎖定它們或從不同線程解鎖它們,將會失敗,但此解決方案依賴於死鎖。

實施例代碼,用一個信號處理程序:

void notify(int, siginfo_t *info, void *) { 
    pthread_mutex_unlock((pthread_mutex_t *) info->si_value); 
} 

void output() { 
    pthread_mutex_t iomutex = PTHREAD_MUTEX_INITIALIZER; 

    struct sigaction act; 
    memset(&act, 0, sizeof(struct sigaction)); 
    act.sa_sigaction = &notify; 
    act.sa_flags = SA_SIGINFO; 
    sigaction(SIGUSR1, &act, NULL); 

    for (...) { 
     pthread_mutex_lock(&iomutex); 

     // do some calculations here... 

     struct aiocb *cblist[]; 
     int cbno; 
     // set up the aio request list - omitted 

     struct sigevent sev; 
     memset(&sev, 0, sizeof(struct sigevent)); 
     sev.sigev_notify = SIGEV_SIGNAL; 
     sev.sigev_signo = SIGUSR1; 
     sev.sigev_value.sival_ptr = &iomutex; 

     lio_listio(LIO_NOWAIT, cblist, cbno, &sev); 
    } 

    // ensure that the last queued operation completes 
    // before this function returns 
    pthread_mutex_lock(&iomutex); 
    pthread_mutex_unlock(&iomutex); 
} 

實施例代碼,用通知功能 - 可能效率較低,因爲額外的線程被創建:

void output() { 
    pthread_mutex_t iomutex = PTHREAD_MUTEX_INITIALIZER; 

    for (...) { 
     pthread_mutex_lock(&iomutex); 

     // do some calculations here... 

     struct aiocb *cblist[]; 
     int cbno; 
     // set up the aio request list - omitted 

     struct sigevent sev; 
     memset(&sev, 0, sizeof(struct sigevent)); 
     sev.sigev_notify = SIGEV_THREAD; 
     sev_sigev_notify_function = &pthread_mutex_unlock; 
     sev.sigev_value.sival_ptr = &iomutex; 

     lio_listio(LIO_NOWAIT, cblist, cbno, &sev); 
    } 

    // ensure that the last queued operation completes 
    // before this function returns 
    pthread_mutex_lock(&iomutex); 
    pthread_mutex_unlock(&iomutex); 
} 

回答

2

如果設置了的sigevent參數在lio_listio()調用中,當一個特定的調用中的所有作業完成時,您將收到一個信號(或函數調用)的通知。你仍然需要:

  1. 等到你,你已經取得lio_listio會將其()調用,要知道當他們全部完成接收許多通知。

  2. 使用一些安全機制從信號處理程序與主線程進行通信,可能是通過全局變量(爲便攜式)進行通信。

如果你在linux上,我會建議將一個eventfd綁定到你的sigevent,然後等待。由於您不需要涉及信號處理程序,因此這更加靈活。在BSD上(但不是Mac OS),您可以使用kqueue等待aiocbs,在solaris/illumos上您可以使用端口來獲取aiocb完成的通知。

如何在Linux上使用eventfds Here's an example

作爲一個方面說明,我想以lio_listio發行工作時要小心。你不能保證它支持多於2 jobs,並且一些系統一次對你能發出的數量限制很低。 Mac OS上的默認值爲16.此限制可以定義爲AIO_LISTIO_MAX宏,但不一定。在這種情況下,您需要調用sysconf(_SC_AIO_LISTIO_MAX)(請參閱docs)。有關詳細信息,請參閱lio_listio documentation

您應該至少從您的lio_listio()調用中檢查錯誤條件。因爲您將同步for循環中的每個循環,並且一次只運行一個循環(除非它是一個遞歸互斥體,但在這種情況下,其狀態可能是如果您的信號處理程序恰好落在另一個線程上,則會損壞)。

更合適的原語可能是一個信號量,它在處理程序中釋放,然後(在for循環之後)獲取與循環相同的次數,調用lio_listio()。但是,如果可以的話,我仍然會推薦一個eventfd。

+0

感謝您提供一篇非常翔實的文章。每個循環的同步都是我想要的,因爲我使用類似於雙緩衝區的東西來存儲計算的數據 - 如果互斥解決方案沒有其他問題,那麼對於我的目的應該沒問題。 我沒有意識到lio_listio()的這些限制 - 我將研究是否使用'mmap'ed文件會更好。 – 2013-02-11 01:15:39

+1

是的,在這種情況下,我會想象你的解決方案與互斥工作正常。我唯一擔心的是在信號處理程序中釋放互斥體的模式,可能在當前阻塞獲取鎖的線程上(對於信號處理程序安全的系統調用列表:http:// pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03)。您在使用傳統信號量的模式中使用互斥鎖。 – Arvid 2013-02-11 01:53:42

+0

確實,它看起來像pthread_mutex_ *函數不是異步信號安全的,而sem_post是。 – 2013-02-11 19:50:45