我正在尝试编写在满足条件时执行的代码。目前,我正在使用while ...循环,我知道效率不高。我也在看AutoResetEvent(),但我不知道如何实现它,以便它一直检查,直到条件为真。
代码也恰好存在于异步方法中,所以可能某种等待可以工作吗?
private async void btnOk_Click(object sender, EventArgs e)
{
// Do some work
Task<string> task = Task.Run(() => GreatBigMethod());
string GreatBigMethod = await task;
// Wait until condition is false
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
}
// Do work
Console.WriteLine("YAY");
}
private bool isExcelInteractive()
{
try
{
Globals.ThisWorkbook.Application.Interactive = Globals.ThisWorkbook.Application.Interactive;
return true; // Excel is free
}
catch
{
return false; // Excel will throw an exception, meaning its busy
}
}
我需要找到一种方法来继续检查isExcelInteractive()
而不会在循环中卡住CPU。
注意:Excel中没有事件处理程序在未处于编辑模式时引发。
答案 0 :(得分:25)
至少你可以将你的循环从忙等待改为慢转。例如:
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
await Task.Delay(25);
}
答案 1 :(得分:8)
private readonly System.Threading.EventWaitHandle waitHandle = new System.Threading.AutoResetEvent(false);
private void btnOk_Click(object sender, EventArgs e)
{
// Do some work
Task<string> task = Task.Run(() => GreatBigMethod());
string GreatBigMethod = await task;
// Wait until condition is false
waitHandle.WaitOne();
Console.WriteLine("Excel is busy");
waitHandle.Reset();
// Do work
Console.WriteLine("YAY");
}
然后其他一些工作需要设置你的处理程序
void isExcelInteractive()
{
/// Do your check
waitHandle.Set()
}
更新: 如果要使用此解决方案,则必须以特定间隔连续调用isExcelInteractive():
var actions = new []{isExcelInteractive, () => Thread.Sleep(25)};
foreach (var action in actions)
{
action();
}
答案 2 :(得分:5)
此实现完全基于Sinaesthetic的实现,但是添加了CancellationToken
并保持相同的执行线程和上下文;也就是说,根据是否需要在同一线程中评估Task.Run()
,将condition
的使用权委派给调用者。
此外,请注意,如果您真的不需要抛出TimeoutException
并且打破循环就足够了,则可能要使用cts.CancelAfter()
或new CancellationTokenSource(millisecondsDelay)
而不是将timeoutTask
与Task.Delay
加Task.WhenAny
一起使用。
public static class AsyncUtils
{
/// <summary>
/// Blocks while condition is true or task is canceled.
/// </summary>
/// <param name="ct">
/// Cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitWhileAsync(CancellationToken ct, Func<bool> condition, int pollDelay = 25)
{
try
{
while (condition())
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
}
catch (TaskCanceledException)
{
// ignore: Task.Delay throws this exception when ct.IsCancellationRequested = true
// In this case, we only want to stop polling and finish this async Task.
}
}
/// <summary>
/// Blocks until condition is true or task is canceled.
/// </summary>
/// <param name="ct">
/// Cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitUntilAsync(CancellationToken ct, Func<bool> condition, int pollDelay = 25)
{
try
{
while (!condition())
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
}
catch (TaskCanceledException)
{
// ignore: Task.Delay throws this exception when ct.IsCancellationRequested = true
// In this case, we only want to stop polling and finish this async Task.
}
}
/// <summary>
/// Blocks while condition is true or timeout occurs.
/// </summary>
/// <param name="ct">
/// The cancellation token.
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <param name="timeout">
/// Timeout in milliseconds.
/// </param>
/// <exception cref="TimeoutException">
/// Thrown after timeout milliseconds
/// </exception>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitWhileAsync(CancellationToken ct, Func<bool> condition, int pollDelay, int timeout)
{
if (ct.IsCancellationRequested)
{
return;
}
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
Task waitTask = WaitWhileAsync(cts.Token, condition, pollDelay);
Task timeoutTask = Task.Delay(timeout, cts.Token);
Task finishedTask = await Task.WhenAny(waitTask, timeoutTask).ConfigureAwait(true);
if (!ct.IsCancellationRequested)
{
cts.Cancel(); // Cancel unfinished task
await finishedTask.ConfigureAwait(true); // Propagate exceptions
if (finishedTask == timeoutTask)
{
throw new TimeoutException();
}
}
}
}
/// <summary>
/// Blocks until condition is true or timeout occurs.
/// </summary>
/// <param name="ct">
/// Cancellation token
/// </param>
/// <param name="condition">
/// The condition that will perpetuate the block.
/// </param>
/// <param name="pollDelay">
/// The delay at which the condition will be polled, in milliseconds.
/// </param>
/// <param name="timeout">
/// Timeout in milliseconds.
/// </param>
/// <exception cref="TimeoutException">
/// Thrown after timeout milliseconds
/// </exception>
/// <returns>
/// <see cref="Task" />.
/// </returns>
public static async Task WaitUntilAsync(CancellationToken ct, Func<bool> condition, int pollDelay, int timeout)
{
if (ct.IsCancellationRequested)
{
return;
}
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
Task waitTask = WaitUntilAsync(cts.Token, condition, pollDelay);
Task timeoutTask = Task.Delay(timeout, cts.Token);
Task finishedTask = await Task.WhenAny(waitTask, timeoutTask).ConfigureAwait(true);
if (!ct.IsCancellationRequested)
{
cts.Cancel(); // Cancel unfinished task
await finishedTask.ConfigureAwait(true); // Propagate exceptions
if (finishedTask == timeoutTask)
{
throw new TimeoutException();
}
}
}
}
}
答案 3 :(得分:3)
今天终于写完了,似乎还可以。您的用法可能是:
await TaskEx.WaitUntil(isExcelInteractive);
public static class TaskEx
{
/// <summary>
/// Blocks while condition is true or timeout occurs.
/// </summary>
/// <param name="condition">The condition that will perpetuate the block.</param>
/// <param name="frequency">The frequency at which the condition will be check, in milliseconds.</param>
/// <param name="timeout">Timeout in milliseconds.</param>
/// <exception cref="TimeoutException"></exception>
/// <returns></returns>
public static async Task WaitWhile(Func<bool> condition, int frequency = 25, int timeout = -1)
{
var waitTask = Task.Run(async () =>
{
while (condition()) await Task.Delay(frequency);
});
if(waitTask != await Task.WhenAny(waitTask, Task.Delay(timeout)))
throw new TimeoutException();
}
/// <summary>
/// Blocks until condition is true or timeout occurs.
/// </summary>
/// <param name="condition">The break condition.</param>
/// <param name="frequency">The frequency at which the condition will be checked.</param>
/// <param name="timeout">The timeout in milliseconds.</param>
/// <returns></returns>
public static async Task WaitUntil(Func<bool> condition, int frequency = 25, int timeout = -1)
{
var waitTask = Task.Run(async () =>
{
while (!condition()) await Task.Delay(frequency);
});
if (waitTask != await Task.WhenAny(waitTask,
Task.Delay(timeout)))
throw new TimeoutException();
}
}
答案 4 :(得分:2)
您可以使用.net框架中内置的SpinUntil。
答案 5 :(得分:2)
试试这个
void Function()
{
while (condition)
{
await Task.Delay(1);
}
}
这将使程序等待,直到条件不为真。 您可以通过添加“!”来反转它前面的条件,以便它会一直等到条件为真。
答案 6 :(得分:0)
在挖掘了很多东西之后,最后,我想出了一个不错的解决方案,它不会挂起CI :)使它适合您的需求!
public static Task WaitUntil<T>(T elem, Func<T, bool> predicate, int seconds = 10)
{
var tcs = new TaskCompletionSource<int>();
using(var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)))
{
cancellationTokenSource.Token.Register(() =>
{
tcs.SetException(
new TimeoutException($"Waiting predicate {predicate} for {elem.GetType()} timed out!"));
tcs.TrySetCanceled();
});
while(!cancellationTokenSource.IsCancellationRequested)
{
try
{
if (!predicate(elem))
{
continue;
}
}
catch(Exception e)
{
tcs.TrySetException(e);
}
tcs.SetResult(0);
break;
}
return tcs.Task;
}
}
答案 7 :(得分:-2)
您可以为此使用异步结果和委托。如果您阅读the documentation,那么它应该清楚地说明该做什么。如果你愿意,我可以写一些示例代码并将其附加到这个答案。
Action isExcelInteractive = IsExcelInteractive;
private async void btnOk_Click(object sender, EventArgs e)
{
IAsyncResult result = isExcelInteractive.BeginInvoke(ItIsDone, null);
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("YAY");
}
static void IsExcelInteractive(){
while (something_is_false) // do your check here
{
if(something_is_true)
return true;
}
Thread.Sleep(1);
}
void ItIsDone(IAsyncResult result)
{
this.isExcelInteractive.EndInvoke(result);
}
如果这段代码没有100%完成,我很抱歉,我不会在这台电脑上安装Visual Studio,但希望它可以让你到达你需要的地方。