Ответ 1
По крайней мере, вы можете изменить свой цикл от оживленного до медленного опроса. Например:
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
await Task.Delay(25);
}
Я пытаюсь написать код, который выполняется при выполнении условия. В настоящее время я использую while... loop, который, как я знаю, не очень эффективен. Я также смотрю на AutoResetEvent(), но я не знаю, как его реализовать, чтобы он продолжал проверять, пока условие не будет истинным.
Кроме того, код живет внутри метода async, так что может быть какой-то ожидание может работать?
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()
без застревания процессора в цикле.
Примечание. В Excel нет обработчика событий, который был бы поднят, когда он не находится в режиме редактирования.
По крайней мере, вы можете изменить свой цикл от оживленного до медленного опроса. Например:
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
await Task.Delay(25);
}
Вы можете использовать обработчик ожидания потока
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();
}
Вы можете использовать SpinUntil, который встроен в .net-framework. Обратите внимание: этот метод вызывает высокую загрузку процессора.
Закончилось писать это сегодня и, кажется, все в порядке. Ваше использование может быть:
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();
}
}
Пример использования: https://dotnetfiddle.net/Vy8GbV
Перекопав много вещей, наконец, я нашел хорошее решение, которое не вешает 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;
}
}
Для этого вы можете использовать результат async и делегат. Если вы читаете документацию, то должно быть ясно, что делать. Я могу написать код примера, если хотите, и прикрепить его к этому ответу.
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 на этом компьютере, но, надеюсь, он доставит вас туда, куда вам нужно добраться.