2012-02-13 42 views
12

我原來的方法是這樣的:DoSomeWork開始對其他線程的一些工作,並返回執行ID(只是隨機字符串)這是確定由TPL任務獲得,從方法返回更多的細節?

string DoSomeWork(); 

方法。後來我可以查詢由給定的執行ID結果。主要的一點是要執行ID可用之前的工作將完成。

現在我想改變簽名返回任務,使用戶可以等待,如果他想。

Task DoSomeWork(); 

同時我還需要返回執行ID(用於跟蹤的目的,例如),我看到幾個選項。首先,如果out參數,二是既執行ID和任務返回元組(在C#這看起來並不是一個最好的選擇),和第三其實我是想請教一下哪些。

如果我將創建類,將來自Task類派生:

public class ExtendedTask : Task 
{ 
    public string ExecutionID {get; set;} 
} 

這是否看起來不錯?或者更好地決定其他選擇?

P.S.在BCL中有一些派生自任務類。

UPDATE,似乎我無法定義這個清晰的界限。但我需要工作之前獲得流程id將完成,所以我不能使用Task.Result

回答

12

我不會親自延長Task<T>,我撰寫它來代替。這樣你就不用擔心任何只返回Task<T>的API - 你可以把這個任務包裝起來。你可以有一個屬性公開潛在的任務,併爲C#5異步的目的,你可以實現你自己的類型awaiter模式 - 但我覺得像創建自己的派生類型可能做更多的傷害比好。儘管這主要是一種直覺。

另一種方法是反過來工作:將您的額外狀態存儲在Task.AsyncState屬性中;畢竟,這就是它的存在。這樣,您可以輕鬆傳遞任務,而不會丟失邏輯上包含的執行上下文。

+0

感謝您的'Task.AsyncState'我沒有意識到它。我只關心它爲什麼反對?有人可以覆蓋它。 – 2012-02-13 18:10:04

+1

@MikeChaliy它旨在實現IAsyncResult,並且是隻讀的。一旦你構建你的任務,任何人都無法在你身上覆蓋它。你需要使用'採取行動'而不是Action'的'爲了得到這套一個構造函數來構造我們的任務... – 2012-02-13 18:12:23

+0

@ReedCopsey UPS,是的,你是對的。那麼,看起來更好。 – 2012-02-13 18:16:34

11

我建議使用Task<T>來代替,因爲它允許您將其他信息「嵌入」任務結果中。

例如,在你的情況下,它可能是有意義的有這樣的:

class ExecutionResult 
{ 
    public int ExecutionID { get; set; } 
    public string Result { get; set; } 
    // ... 
} 


public Task<ExecutionResult> DoSomeWork() 
{ 
    return Task.Factory.StartNew(() => 
    { 
      // Replace with real work, etc... 
      return new ExecutionResult { ExecutionID = 0, Result = "Foo" }; 
    }); 
} 

編輯迴應評論:

如果您需要的任務「之前」的數據完成,並試圖訪問此用於其他目的,我會建議做一個類包含任務和其他數據,並返回它,即:

class ExecutionResult 
{ 
    public int ExecutionID { get; private set; } 
    public Task<string> Result { get; private set; } 
    // ... Add constructor, etc... 
} 


public ExecutionResult DoSomeWork() 
{ 
    var task = Task.Factory.StartNew(() => 
    { 
      // Replace with real work, etc... 
      return "Foo"; 
    }); 

    return new ExecutionResult(1, task); // Make the result from the int + Task<string> 
} 

這仍然允許您訪問有關您的過程的信息,以及Task/Task<T>

+0

作業完成後'Task.Result'將可用。我之前需要這個值。 – 2012-02-13 17:58:55

+1

@MikeChaliy編輯,以顯示我會怎麼處理它在這種情況下... – 2012-02-13 18:04:26

+0

謝謝您的回答。 – 2012-02-13 18:07:10

1

如果決定從TaskTask<TResult>繼承,你可能會遇到挫折的Action<Object>Func<Object,TResult>委託,它提供了必須指定當時的任務,實際工作中的任務派生對象被構造,並且以後不能更改。即使基類構造函數不是新創建的任務,也是如此,事實上,它可能很晚纔會啓動,如果有的話。

這使得在需要創建實例之前需要創建實例的情況下,很難使用Task派生類,因爲實例必須在其最終工作的完整詳細信息可用之前創建。

一個例子可能是在一個共同的目標工作衆所周知Task<TResult>節點的無定形網絡,使得它們訪問在特設方式彼此的Result屬性。確保網絡中任意節點上可以使用Wait()的最簡單方法是在啓動它們之前預先構建所有節點。這很好地避免了嘗試分析工作圖相關性的問題,並且允許運行時因素確定何時,如何以及以何種順序要求值。

這裏的問題是,對於某些節點,您可能不能夠提供定義在施工時工作中的作用。如果創建必要的lambda函數需要從網絡中的其他任務結束了Result數值,提供我們想要的ResultTask<TResult>可能尚未建立呢。即使它恰好是在施工前階段早期構建的,您也不能在其上調用Start(),因爲它可能會將依賴關係併入其他沒有的節點。請記住,構建網絡的關鍵在於避免這些複雜性。

如果這還不夠,還有其他的原因是不方便,要使用lambda功能提供所需的功能。因爲它傳遞到構造函數作爲參數,函數不能訪問this指針最終任務實例,這使得醜陋的代碼,特別是考慮的範圍之內必然定義的拉姆達 - 可能無意關閉過 - 一些不相關的this指針。

我可以繼續下去,但底線是,你不應該在派生類中定義的擴展功能時要忍受運行關閉膨脹等的麻煩。這不會錯過多態性的全部點嗎?以正常的方式定義一個Task派生類的工作委託,即基類中的一個抽象函數會更加優雅。

這裏是如何做到這一點。訣竅是定義一個私有構造函數,它關閉其自己的一個參數。最初設置爲null的參數充當佔位符變量,您可以關閉它以創建基類Task所需的代理。一旦你在構造函數體中,'this'指針可用,所以你可以修補實際的函數指針。

對於從 '任務' 導出:

public abstract class DeferredActionTask : Task 
{ 
    private DeferredActionTask(DeferredActionTask _this) 
     : base(_ => ((Func<DeferredActionTask>)_)().action(), 
       (Func<DeferredActionTask>)(() => _this)) 
    { 
     _this = this; 
    } 
    protected DeferredActionTask() : this(null) { } 

    protected abstract void action(); 
}; 

對於從 '任務<TResult>' 導出:

public abstract class DeferredFunctionTask<TResult> : Task<TResult> 
{ 
    private DeferredFunctionTask(DeferredFunctionTask<TResult> _this) 
     : base(_ => ((Func<DeferredFunctionTask<TResult>>)_)().function(), 
       (Func<DeferredFunctionTask<TResult>>)(() => _this)) 
    { 
     _this = this; 
    } 
    protected DeferredFunctionTask() : this(null) { } 

    protected abstract TResult function(); 
}; 

[編輯:簡化]

這些簡化版本通過直接關閉派生實例'動作函數方法直接關閉來進一步減少無關閉包。這也可以釋放基類中的AsyncState以防您想要使用它。由於你現在擁有了你自己的完整的派生類,所以幾乎沒有必要。因此,AsyncState未傳遞到工作函數中。如果你需要它,你總是可以從基類的屬性中抓取它。最後,現在可以將各種可選參數傳遞給基類Task

對於從 '任務' 導出:

public abstract class DeferredActionTask : Task 
{ 
    private DeferredActionTask(Action _a, Object state, CancellationToken ct, TaskCreationOptions opts) 
     : base(_ => _a(), state, ct, opts) 
    { 
     _a = this.action; 
    } 

    protected DeferredActionTask(
      Object state = null, 
      CancellationToken ct = default(CancellationToken), 
      TaskCreationOptions opts = TaskCreationOptions.None) 
     : this(default(Action), state, ct, opts) 
    { 
    } 

    protected abstract void action(); 
}; 

對於從 '任務<TResult>' 導出:

public abstract class DeferredFunctionTask<TResult> : Task<TResult> 
{ 
    private DeferredFunctionTask(Func<TResult> _f, Object state, CancellationToken ct, TaskCreationOptions opts) 
     : base(_ => _f(), state, ct, opts) 
    { 
     _f = this.function; 
    } 

    protected DeferredFunctionTask(
      Object state = null, 
      CancellationToken ct = default(CancellationToken), 
      TaskCreationOptions opts = TaskCreationOptions.None) 
     : this(default(Func<TResult>), state, ct, opts) 
    { 
    } 

    protected abstract TResult function(); 
}; 
0
private async DeferredFunctionTask<int> WaitForStart(CancellationTokenSource c, string serviceName) 
    { 


     var t = await Task.Run<int>(() => 
     { 
      int ret = 0; 
      for (int i = 0; i < 500000000; i++) 
      { 


       //ret += i; 
       //if (i % 100000 == 0) 
       // Console.WriteLine(i); 

       if (c.IsCancellationRequested) 
       { 
        return ret; 
       } 
      } 

      return ret; 

     }); 


     return t; 
    } 

錯誤CS1983一個異步的返回類型方法必須無效,任務或任務