2017-07-07 44 views
0

我有以下代碼:轉換的Thread.join(延時),以相當於非線程模式

Thread validationThread = new Thread(DoAsyncValidationMethod); 

validationThread.Start(threadParams); 

bool isAborted = false; 

int timeLeft = 5000; 

// wait for thread terminates itself 
if (!validationThread.Join(timeLeft)) 
{ 
    // timeout, abort thread 
    validationThread.Abort(); 
    isAborted = true; 
} 

換句話說,我做了應該在指定的時間間隔來完成(一些工作5000毫秒)。如果完成或不完成,採取的線程方法將保證代碼在啓動後不遲於5秒返回。

有沒有辦法用異步/等待模式來完成這個?或者因爲線程只是用於訣竅,還有其他方法可以採用嗎?

越優雅簡單越好。 謝謝你的幫助。

+0

請永遠不調用'Thread.Abort的()'除非你試圖強行碰撞出你的應用程序中。調用中止可能會破壞.NET運行時的狀態,導致**所有其他線程**的結果不可靠。 – Enigmativity

回答

2

這是我在其中一個項目中使用的一段代碼。

更多的,看看Task.WhenAny(param Task[] task) method on MSDN。它返回已完成的任務。

var timeout = 5000; 
var task = Task.Run(() => { 
    Thread.Sleep(new Random().Next(3000, 7000));//Running time simulation! 
    return "Say my name!";//Breaking Bad! 
}); 
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { 
    // Task completed within timeout. 
    // What if the task is faulted or canceled. 
    // I did not supply a cancellation token but it can easily be added 
    // We re-await the task so that any exceptions/cancellation is rethrown. 
    await task; 
} 
else { 
    // timeout/cancellation logic 
} 
+0

謝謝Emrah,很聰明的第二個任務參數。 – Alex

+0

@Alex,我希望它有幫助!我不確定這個代碼在一個緊密的循環中的性能,但我迄今爲止沒有問題使用過這個(*'ω'*) –

+0

@EmrahSüngü - 'new Random()。Next(3000,7000)'in a tight循環可能是一個壞主意。 – Enigmativity

0

你可以用微軟的Reactive Framework(NuGet「System.Reactive」)很好地做到這一點。那麼你可以這樣做:

IObservable<bool> query = 
    Observable.Amb(
     Observable.Timer(TimeSpan.FromSeconds(5.0)).Select(_ => false), 
     Observable.Start(() => DoAsyncValidationMethod()).Select(_ => true)); 

IDisposable subscription = 
    query.Subscribe(flag => 
    { 
     /* `true` succeeded, `false` timed out */ 
    }); 
+0

爲什麼投票反對?這工作得很好。這段代碼有什麼問題? – Enigmativity

+0

我們開始在Unity項目中使用Reactive解決方案。不同風格的思維風格,但它很有趣! –

+0

@EmrahSüngü - 它通常比TPL更強大。 – Enigmativity

2

使用CancellationTokenSource。該來源是CancellationTokens的工廠。您將此令牌傳遞給您希望能夠以簡潔的方式取消其處理的所有人。如果要取消所有具有CancellationTokenCancellationTokenSource的進程,只需告知CancellationTokenSource將取消發送到它生成的所有CancellationTokens,從而取消所有具有來自此源令牌的進程。

而好的是,CancellationTokenSource有一個CancelAfter(some timeout)

它的良好實踐,讓您的流程啓動器爲您提供cancellationToken,因此您的流程啓動器可以決定在一次調用中要取消哪些流程。

public async Task<MyResult> MyProcessAsync(CancellationToken cancellationToken, ...) 
{ 
    // do something lengthy processing, without await, regularly check if Cancellation requested 
    while (stillProcessing) 
    { 
     cancellationToken.ThrowIfCancellationRequested(); 
     ... // process 
    } 

    // do some processing with async-await: 
    await myDbContext.SaveChangesAsync(cancellationToken); 
} 

用法:

private async Task LengthyProcessing(...) 
{ 
    CancellationTokenSource tokenSource = new CancellationTokenSource(); 
    tokenSource.CancelAfter(TimeSpan.FromSeconds(5)); 

    CancellationToken myCancellationToken = tokenSource.Token 
    try 
    { 
     // Start one task, don't await yet: 
     var myTask = await MyProcessAsync(myCancellationToken, ...) 
     // during processing there will be regular check if cancellation requested 
     // meanwhile, whenever myTask has to await, I can do some processing 
     // I'll have to check for cancellation regularly also: 

     while(...) 
     { 
      myCancellationToken.ThrowIfCancellationRequested(); 
      DoSomeProcessing(); 
      ... 
     } 

     MyResult result = await myTask; 
     // if here, not cancelled. can use Result: 
     ProcessResult(result); 
    } 
    catch (OperationCanceledException exc) 
    { 
     ProcessOperationCanceled(); 
    } 
} 

請注意,只有一個CancelltionTokenSource使用。無論何時此源認爲某些內容應該被取消,所有來自該源的令牌都會收到有關取消請求的通知。

而不是通過CancellationToken.ThrowIfCancellationRequested()使用異常處理考慮使用bool CancellationToken.IsCancellationRequested

MSDN how to cancel a task and its children

+0

@哈拉爾 - 謝謝你的回答。這種方法不會創建一個新的線程,這一切都發生在主線程中,對嗎? – Alex

+0

沒關係,我查看了鏈接中的代碼,並意識到任務將用於代碼中'... // process'部分。 – Alex