簡體   English   中英

C#——將同步程序轉換為異步程序

[英]C# - converting synchronous program into asynchronous program

我目前正在重寫我的代碼以添加異步執行。 但是,我在調整一組方法時遇到了麻煩。

在提問之前,我查看了一些類似這樣的問題,但問題通常是使用 Task.Run() 而不是 async/await。

=======摘要=======

我的程序將用於自動化帶有多個閥門的測試台。 因此,我編寫了諸如open/closeValve() 之類的方法,可將閥門打開或關閉到某個閾值,將 Flow () 增加/減少到 select 以打開或關閉哪個閥門,以及調用 increqse/decreaseFlow() 的調節 ()方法。

就在最近,在閱讀了它之后,我決定添加異步執行是一個實時顯示有關工作台數據的好主意。 但是我的方法都被認為是以同步方式工作的,所以我很難適應它們。

=======代碼=======

例如這里是 openValve() 方法

public int openValve(int limit)
    {
        if(_masterFlowrate >= _instructions - 0.1 && _masterFlowrate <= _instructions + 0.1)
        {
            //flowrate ~= instructions => no need to manipulate the valves
            return 1;
        }
        else
        {
            if(valveOpening < limit)
            {
                //flowrate < instructions & valve opened less than the limit
                // => opening valve untill the limit or untill flowrate = instructions
                valveOpening++;
 
                //Opening the valve by writing the value in a command computer via Modbus
                master.WriteSingleRegister(1, _mdbAdr, Convert.ToUInt16(ouvertureValve));
                return 0;
            }
            else
            {
                //flowrate < instructions & valve already opened beyond the limit
                // => not manipulating the valve
                return -1;
            }
        }

為了使該方法適應異步執行,我將返回值類型從int更改為Task<int>並將return 1更改為return Task.FromResult(1)例如。

現在直接使用上一個的 increaseFlow() 方法看起來是這樣的(Vp、Vm 和 Vg 是 Valve 類的實例)

 public int increaseFlow()
    {
        int retVal = 0;
        switch(openingStep)
        {
            case 1:
                retVal = Vp.openValve(60);
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 2;
                }
                break;

            case 2:
                retVal = Vg.openValve();
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 3;
                }
                break;

            case 3:
                retVal = Vm.openValve();
                if(retVal > 0) openingStep = 4;
                else
                {
                    if(retVal < 0) openingStep = 4;
                }
                break;
        }

        return retVal;
    }

這就是我開始遇到麻煩的地方。 我試過改變int retVal = 0; 進入var retVal = Task.FromResult(0)不會給我任何編譯器錯誤。

然后在每個 openValve() 之前添加 awaits 關鍵字,但這給了我以下信息:

Error   CS0029  Cannot implicitly convert type 'int' to 'System.Threading.Tasks.Task<int>'

然后regular() 方法調用increaseFlow()。

=======結論=======

這就是我開始認為代碼可能沒有考慮到異步工作並且需要重新排列的地方。 這真的是我的問題,還是有不需要完全重做方法的解決方案。 我並不是說我不會這樣做,但我很想知道結構是否錯誤,或者我只是沒有使用正確的關鍵字/方法等......

添加異步執行是實時顯示有關工作台數據的好主意

這並不是異步執行的真正目的。 異步就是釋放線程,這在客戶端轉化為響應式 UI,在服務器端轉化為更具可擴展性的服務。

關於“實時顯示有關工作台的數據”,聽起來您真正想要的是一種進度報告形式。 (請注意,此上下文中的“進度”可以是任何數據集合,而不僅僅是完成百分比)。 內置的IProgress<T>是為進度報告而設計的,即使大多數示例都是針對異步代碼的,也可以很好地與同步代碼配合使用。

如果您的應用主要發出 I/O 綁定請求,您需要異步代碼。 但是使某些東西異步的正確方法是從最低級別開始,例如WriteSingleRegister ,然后讓異步從那里通過應用程序發展起來。 如果您正在控制硬件,異步是可能的,但可能不是最佳解決方案。

一個可能的原因是async Task<T>Task<T>之間的區別。 IE

public async Task<int> MyMethod(){
    return 42; // Task creation is implicit
}

public Task<int> MyMethod(){
    return Task.FromResult(42); // Task creation is explicit
}

更大的問題是為什么要使用異步方法重寫系統。 通常有幾個主要原因:

  1. 您正在使用某種 IO、磁盤、網絡等。異步方法有助於避免在使用這些方法時阻塞線程。
  2. 您正在使用計算密集型的東西,並且不想在 CPU 處理幾秒鍾時阻塞 UI。
  3. 在線程中包裝阻塞 IO 調用以避免阻塞 UI。 (當真正的異步 IO api 不可用時)。

只是將方法更改為異步並使用Task.FromResult可能根本不會帶來任何優勢,因為所有代碼仍將在主線程上運行,並且任何 CPU 密集型操作或阻塞調用仍將凍結 UI。

如果目標是避免 UI 阻塞,那么Task.Run可能是通往 go 的方法。 但這將要求您的程序是線程安全的。 您可以使用limitedConcurrencyTaskScheduler在后台線程上移動工作,但仍會阻止同時執行多個命令。 這降低了線程安全問題的風險。

總的來說,我建議重新分析您要解決的實際問題。 使用異步模式可能是這種解決方案的一部分,但它可能不是唯一的方法。

暫無
暫無

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

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