将Thread.Join(timeLapse)转换为等效的非线程模式

时间:2017-07-07 00:50:01

标签: c# multithreading async-await

我有以下代码:

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秒返回。

有没有办法用async / await模式完成这个?或者因为线程只是用于技巧,还有其他方法吗?

越优雅,越简单越好。 谢谢你的帮助。

3 个答案:

答案 0 :(得分:2)

以下是我在其中一个项目中使用的一段代码。

更多信息,请查看Task.WhenAny(param Task[] task) method on MSDN。它返回完成的任务。

class SongsViewController {
var currentHeaderHeight: CGFloat = 136

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //Throws error
        guard let currentHeaderHeight = self.songsTable.headerView(forSection: 0)?.frame.size.height else {
            print("header view has no height")
            return
        }
    }

    public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return currentHeaderHeight
    }

    public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = UIView()
        headerView.frame = CGRect(x: 0, y: 0, width: Screen.width, height: maxHeaderHeight)
        return headerView
    }
}

答案 1 :(得分:2)

使用CancellationTokenSource。源是CancellationTokens的工厂。您将此令牌传递给您希望能够以整洁的方式取消其处理的每个人。如果您要取消同一CancellationTokenCancellationTokenSource的所有流程,只需告诉CancellationTokenSource向其生成的所有CancellationTokens发送取消,从而取消所有流程从这个来源获得令牌。

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

这是一个很好的做法,让您的流程启动器为您提供cancelToken,这样您的流程启动器就可以决定在一次调用中取消哪些流程。

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

答案 2 :(得分:0)

您可以使用Microsoft的Reactive Framework(NuGet&#34; System.Reactive&#34;)来做到这一点。然后你可以这样做:

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 */
    });