2016-06-09 32 views
1

我想前言這個問題有以下幾點:在TPL下啓動阻塞I/O請求的線程如何立即返回?

  1. 我熟悉的IAsyncStateMachine實施,在C#中的關鍵字await產生。

  2. 我的問題是不是能夠確保當您使用asyncawait關鍵字控制的基本流程。

假設一個

在任何線程環境的默認線程行爲,無論是在Windows操作系統級別或POSIX系統或.NET線程池,一直認爲當線程發出請求進行I/O綁定操作,例如對於磁盤讀取,它向磁盤設備驅動程序發出請求,並且進入等待狀態。當然,我在細節上留言,因爲它們不是我們討論的時刻。

重要的是,該線程只能通過設備驅動程序的中斷來解除阻塞,直到它通知完成。在此期間,線程保持在等待隊列上,並且不能重新用於其他任何工作。

我首先想要確認上面的描述。

假設乙

其次,即使引進第三方物流,其增強在.NET框架V4.5完成,並與涉及任務異步操作語言層面的支持,這種默認行爲描述於假設A尚未更改。

問題

然後,我不知所措試圖調和假設A和B與要求突然在所有TPL文獻中出現了:

當,說,主線程啓動這個請求,爲這個I/O綁定的 工作,它立即返回並繼續執行消息泵中排隊的消息的其餘部分 。

那麼,是什麼讓那個線程返回回去做其他工作?是不是該線程應該在等待狀態等待隊列

您可能會試圖回覆狀態機中的代碼啓動任務awaiter,並且如果awaiter尚未完成,主線程將返回。

乞丐的問題 - 請求者運行什麼線程?

而引發思考的答案是:無論該方法的實施情況如何,其任務等待的是誰的任務。

這會讓我們更加深入兔子洞,直到我們到達實際提供I/O請求的最後一個實現。

.NET框架中的那部分源代碼在哪裏改變了關於線程如何工作的基礎機制?

旁註

雖然有些堵異步方法,如WebClient.DownloadDataTaskAsync,如果一個人通過遵循他們的代碼 他們(該方法的,而不是自己)橢圓形道到他們 腸,一會看到他們最終要麼同步執行 下載,如果請求同步執行 (Task.RunSynchronously())或者如果異步請求,則阻止當前線程 offloa d阻塞I/O使用 異步編程模型(APM)BeginEnd方法綁定調用線程池線程。

這無疑將導致主線程立即因爲 它只是卸載阻塞I/O工作返回線程池線程,從而 增加約diddlysquat應用程序的可擴展性。

但是這是一個案例,在獸的腸內,工作 被祕密卸載到線程池線程。在API 不這樣做的情況下,說看起來像這樣的API:

public async Task<string> GetDataAsync() 
{ 
    var tcs = new TaskCompletionSource<string>(); 

    // If GetDataInternalAsync makes the network request 
    // on the same thread as the calling thread, it will block, right? 
    // How then do they claim that the thread will return immediately? 
    // If you look inside the state machine, it just asks the TaskAwaiter 
    // if it completed the task, and if it hasn't it registers a continuation 
    // and comes back. But that implies that the awaiter is on another thread 
    // and that thread is happily sleeping until it gets a kick in the butt 
    // from a wait handle, right? 
    // So, the only way would be to delegate the making of the request 
    // to a thread pool thread, in which case, we have not really improved 
    // scalability but only improved responsiveness of the main/UI thread 
    var s = await GetDataInternalAsync(); 

    tcs.SetResult(s); // omitting SetException and 
        // cancellation for the sake of brevity 

    return tcs.Task; 
} 

如果我的問題似乎是荒謬請溫柔的我。幾乎所有事情的事物知識程度都是有限的。我只是在學習任何東西。

+2

我認爲bodangly的工作做得很好,但爲了完整起見,這裏有一個鏈接到[POSIX Async IO]的手冊頁(http://man7.org/linux/man-pages/man7/aio.7。 html)(在Linux內),再次證明假設A是如何破解的。 –

+0

@Damien_The_Unbeliever哇!謝謝。那爲我做了。 –

+0

也看到我的答案在這裏:http://stackoverflow.com/questions/37419572/if-async-await-doesnt-create-any-additional-threads-then-how-does-it-make-appl/37419845#37419845 –

回答

2

當您在談論異步I/O操作時,Stephen Cleary(http://blog.stephencleary.com/2013/11/there-is-no-thread.html)指出的事實是,沒有線程。異步I/O操作在比線程模型更低的級別上完成。它通常發生在中斷處理程序例程中。因此,沒有處理請求的I/O線程。

您詢問啓動阻塞I/O請求的線程是如何立即返回的。答案是因爲I/O請求不在實際阻塞的核心。你可以阻塞一個線程,這樣你就可以直到I/O請求完成時纔有意不要做任何其他事情,但它永遠不是被阻塞的I/O,它是決定旋轉的線程(或者可能產生其時間片)。

線程立即返回,因爲沒有必要在輪詢或查詢I/O操作。這是真正的異步性的核心。一個I/O請求被完成,最終完成從ISR中冒出來。是的,這可能會導致線程池中設置任務完成,但這發生在幾乎不可察覺的時間量。工作本身從來不需要在一個線程上運行。請求本身可能已經從線程發出,但由於它是一個異步請求,線程可以立即返回。

讓我們暫時忘記C#吧。假設我正在寫一些嵌入式代碼,並要求SPI總線上的數據。我發送請求,繼續我的主循環,當SPI數據準備就緒時,會觸發一個ISR。我的主循環會立即恢復,因爲我的請求是異步的。它所要做的就是將一些數據放入移位寄存器並繼續。當數據準備好讓我回讀時,會觸發一箇中斷。這不是在一個線程上運行。它可能會中斷一個線程來完成ISR,但你不能說它實際上在該線程上運行。僅僅因爲它的C#,這個過程並沒有什麼不同。

同樣,讓我們​​說我想通過USB傳輸數據。我將數據放在DMA位置,設置一個標誌告訴總線轉移我的URB,然後立即返回。當我收到響應後,它也會被移入內存,發生中斷,並設置一個標誌讓系統知道嘿,然後爲你放置一個數據包。

所以再一次,I/O永遠不會真正阻塞。它可能會阻止,但這不是在低層次上發生的事情。它是更高級別的進程,可能決定一個I/O操作必須與其他一些代碼同步發生。這並不是說I/O是即時的。只是CPU沒有停止工作來維護I/O。如果以這種方式實現,它可能會阻塞,並且這可能涉及線程。但這不是如何實現異步I/O的。

+0

謝謝。我曾閱讀過這篇文章,但這並沒有回答我的問題的核心。那篇文章沒有回答基本的問題 - 哪個是提供I/O請求的線程?線程在交付請求後不久會發生什麼?關鍵是:完成I/O請求的工作不是任何線程的業務,這根本不是我的問題。 –

+0

而且總體而言,我認爲,在「整個操作中根本不涉及任何線程」的小細節有點誤導。 –

+0

事實上,我所尋找的解釋被埋在了這篇文章中非常需要的闡述中,*「在IRP」懸而未決「的情況下,操作系統返回到庫,它將一個不完整的任務返回到按鈕點擊事件處理程序,它暫停異步方法,並且UI線程繼續執行。「* –

相關問題