簡體   English   中英

無法使用pthread_kill和sigwait解除阻止/“喚醒”線程

[英]Can't unblock/“wake up” thread with pthread_kill & sigwait

我正在研究C / C ++網絡項目,並且在同步/給線程發送信號方面遇到困難。 這是我要完成的工作:

  1. 使用poll函數輪詢一堆套接字
  2. 如果在POLLIN事件中准備好任何套接字,則將信號發送到讀取器線程和寫入器線程以“喚醒”

我有一個稱為MessageHandler的類,該類設置信號掩碼並生成讀取器和寫入器線程。 然后,在它們內部,我等待應該喚醒它們的信號。

問題是我正在通過向線程發送信號來測試所有這些功能,但它永遠不會喚醒。

這是帶有進一步說明的問題代碼。 注意,我剛剛強調了它如何與閱讀器線程一起工作,因為編寫器線程本質上是相同的。

// Called once if allowedSignalsMask == 0 in constructor
// STATIC
void MessageHandler::setAllowedSignalsMask() {
     allowedSignalsMask = (sigset_t*)std::malloc(sizeof(sigset_t));
     sigemptyset(allowedSignalsMask);
     sigaddset(allowedSignalsMask, SIGCONT);
}

// STATIC
sigset_t *MessageHandler::allowedSignalsMask = 0;

// STATIC
void* MessageHandler::run(void *arg) {
    // Apply the signals mask to any new threads created after this point
    pthread_sigmask(SIG_BLOCK, allowedSignalsMask, 0);

    MessageHandler *mh = (MessageHandler*)arg;
    pthread_create(&(mh->readerThread), 0, &runReaderThread, arg);

    sleep(1); // Just sleep for testing purposes let reader thread execute first
    pthread_kill(mh->readerThread, SIGCONT);
    sleep(1); // Just sleep for testing to let reader thread print without the process terminating

    return 0;
}

// STATIC
void* MessageHandler::runReaderThread(void *arg) {
    int signo;
    for (;;) {
            sigwait(allowedSignalsMask, &signo);

            fprintf(stdout, "Reader thread signaled\n");
    }

    return 0;
}

我刪除了代碼中的所有錯誤處理以壓縮它,但確實知道線程正常啟動並進入sigwait調用。

該錯誤可能很明顯(它不是語法錯誤-上面的代碼是由可編譯代碼壓縮而成的,我可能在編輯時將其搞砸了),但由於我花了很多時間,我似乎還是找不到/看到它時間在這個問題上,使自己困惑。

讓我解釋一下我認為我在做什么以及是否有意義。

  1. 創建類型為MessageHandler的對象后,它將把allowedSignalsMask設置為我感興趣的用於喚醒線程的一個信號(暫時)的集合。
  2. 我使用pthread_sigmask將信號添加到當前線程的阻塞信號中。 此后創建的所有其他線程現在應該具有相同的信號掩碼。
  3. 然后,我使用pthread_create創建讀取器線程,其中arg是指向MessageHandler類型的對象的指針。
  4. 我稱睡眠為一種便宜的方法,以確保我的readerThread一直執行到sigwait()
  5. 我將信號SIGCONT發送到readerThread,因為我對sigwait感興趣,一旦收到它就喚醒/取消阻止。
  6. 再次,我將睡眠稱為一種廉價的方法,以確保我的readerThread在從sigwait()喚醒/解除阻塞后可以執行所有方法。

其他有用的注釋可能有用,但我認為不會影響該問題:

  • 構造MessageHandler,然后在給定要運行的函數指針的情況下創建另一個線程。 此線程將負責創建讀取器和寫入器線程,使用poll函數輪詢套接字,然后可能同時向讀取器和寫入器線程發送信號。

我知道這是一篇很長的文章,但是非常感謝您閱讀並提供任何幫助。 如果我不夠清楚,或者您覺得我沒有提供足夠的信息,請告訴我,我將予以糾正。

再次感謝。

POSIX線程具有條件變量是有原因的。 使用它們。 使用線程編程時,您不需要信號黑客來完成基本的同步任務。

這是一個很好的pthread教程,其中包含有關使用條件變量的信息:

https://computing.llnl.gov/tutorials/pthreads/

或者,如果您對信號量更滿意,則可以改用POSIX信號量( sem_initsem_postsem_wait )。 但是一旦弄清楚了為什么條件變量和互斥鎖配對有意義,我想您會發現條件變量是一個更加方便的原語。

另外,請注意,您的當前方法每次同步都會引發多個系統調用(用戶空間/內核空間轉換)。 通過良好的pthreads實現,使用條件變量應該最多將其降至一個syscall,並且如果線程之間的相互配合程度足夠好以至於當它們仍在用戶空間中旋轉時發生等待事件,則可能根本不進行任何調用。 。

這種模式似乎有點奇怪,並且很可能容易出錯。 pthread庫包含許多同步方法,最有可能滿足您需求的方法是pthread_cond_*系列。 這些方法處理條件變量 ,這些條件變量實現了Wait and Signal方法。

使用SIGUSR1而不是SIGCONT。 SIGCONT不起作用。 也許信號專家知道原因。

順便說一下,我們使用這種模式是因為條件變量和互斥體對於我們的特定應用程序來說太慢了。 我們需要非常快速地睡眠和喚醒各個線程。

R.指出由於額外的內核空間調用而產生了額外的開銷。 也許如果您睡眠> N個線程,則單個條件變量將擊敗多個sigwait和pthread_kills。 在我們的應用程序中,我們只想在工作到達時喚醒一個線程。 您必須為每個線程具有一個條件變量和互斥體才能執行此操作,否則將獲得踩踏信號。 在一項測試中,我們睡了N次並喚醒N個線程,信號比互斥量和條件變量高出5倍(原本可能是40倍,但我不記得了。。。) 我們沒有測試過一次可以喚醒1個線程的Futex,並且專門對其進行了編碼以限制對內核空間的訪問。 我懷疑futex會比互斥體快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM