创建等待任务,循环到true或超时

时间:2015-09-16 05:38:23

标签: c# async-await

我试图使用下面的函数不返回true / false,除非Boolean Function arg返回true或超时到期。在当前状态下,如果布尔函数arg返回false,则立即返回false而不是循环并重试X更多毫秒。

 public delegate bool BooleanFunction ();

    public static async Task<bool> Wait(uint Milliseconds, BooleanFunction Function)
    {
        var StartTime = Environment.TickCount;

        do
        {
            if (Function())
            {
                return true;
            }

            Thread.Yield();
        }
        while (Environment.TickCount < StartTime + Milliseconds);

        return false;
    }

2 个答案:

答案 0 :(得分:3)

您需要使用await Task.Yield代替Thread.Yield

    if (Function())
    {
        return true;
    }

    await Task.Yield();

如果您还想处理将异步委托传递给Wait,请保留现有版本并添加以下重载:

public static async Task<bool> Wait(uint Milliseconds, Func<Task<bool>> Function)
{
    var StartTime = Environment.TickCount;

    do
    {
        if (await Function())
        {
            return true;
        }

        Thread.Yield();
    }
    while (Environment.TickCount < StartTime + Milliseconds);

    return false;
}

然后你可以这样做:

   var result = await Wait(10000, async () => await Test());     

答案 1 :(得分:1)

Microsoft提出了另一种实现方法,即使用cancellationTokenSource和cancellationToken。

Microsoft about Task Cancellation

StackOverflow也有一些关于此的主题:

CancellationToken and CancellationTokenSource-How to use it?

转换为您的问题,调用者不会传递委托函数,而是传递System.Threading.CancellationToken对象。 Wait函数不是调用委托,而是定期检查CancellationToken对象是否请求取消。

这种方法与你的方法非常相似,只是它是一种更标准的模式。真正的好处是,您可以使用取消在一次通话中使用来自同一来源的令牌的所有任务。此外,它还具有内置超时功能。

另一个优点是取消的原因是在你的函数之外决定,它可以是超时,或者因为BooleanFunction返回true,甚至可能因为你的程序停止等等。即使在将来的版本中,停止等待的新理由是介绍,你的等待功能不必改变。它只是做它的工作,并定期检查CancellationIsRequested。

示例代码:

你的等待功能不会做很多事情,而是等待。我想你简化了这个,因为如果真的需要它,我猜你曾经使用过System.Timer。我们假设你必须做点什么

我有一个带有两个按钮的表单:buttonStartWait和buttonCancelWait。按下时,buttonStartWait将启动Wait,而buttonCancelWait将取消等待。

如果我们使用了你的方法,buttonCancelWait会使委托返回true。

代码如下(我也将Ticks的用法改为TimeSpan以便于阅读)

private CancellationTokenSource tokenSource = null;

private async void OnButtonStartWait_clicked(object sender, ...)
{
    this.buttonStartWait.Enabled = false;
    TimeSpan waitTime = GetWaitTime();        
    this.tokenSource = new tokenSource(waitTime);
    // this makes sure that after waitTime cancellation is requested
    await this.Wait(this.TokenSource.Token);
    this.buttonstartWait.Enabled = true;
}

private async Task<bool> Wait(CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {   // do the thing your function is supposed to do.
        DoSomethingShort();

        // if this function takes some time, pass the token to it
        // and let it regularly check if cancellation is requested
        DoSomethingLonger(token);

        // or consider starting a Task which regularly checks
        // if cancellation is requested
        await Task.Run( () => DoSomethingLonger(token), token);
    }
    return token.IsCancellationRequested;
}

// a Wait function that really doesn't do anything but wait
// until cancellation is requested.
private async Task<bool> Wait(CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {   // wait a short while
        await Task.Wait(TimeSpan.FromSeconds(0.1), token;
    }
    return token.IsCancellationRequested;
}

private async void OnButtonCancelWait_clicked(object sender, ...)
{
    if (this.TokenSource != null)
    {
        this.TokenSource.Cancel();
    }
}